<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ペンギンの巣</title><description>No description</description><link>https://jerryblack.vercel.app/</link><language>zh_CN</language><item><title>diss_quack and Array Game (CF 2246 D)</title><link>https://jerryblack.vercel.app/posts/diss-quack-and-array-game-en/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/diss-quack-and-array-game-en/</guid><description>Game theory + bitwise enumeration — enumerate how many global halving operations are performed to compute the minimum step count.</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/2246/problem/D&quot;&gt;Codeforces Round 1108 (Div. 2) D&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Problem Summary&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;You are given an array $a$ of length $n$ with $0 \leq a_i \leq 10^5$. Before the game starts, Alice may increment any single element any number of times, each increment counted as one step. Then Alice and Bob alternate turns, with Bob moving first. On Bob&apos;s turn he picks two positions and swaps them (possibly the same position). On Alice&apos;s turn: if $a_1$ is even, she picks the largest $j$ such that $a_i \bmod 2 = 0$ for all $i \leq j$, then divides every $a_i$ ($i \le j$) by $2$ — this counts as one step; otherwise she decreases $a_1$ by $1$, again one step. When any value becomes zero it is removed from the array. Bob wants to maximise Alice&apos;s total number of steps, Alice wants to minimise it. Output the minimum number of steps Alice performs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Approach&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;First consider the version without the pre-game &lt;code&gt;+1&lt;/code&gt; operations. If there is any odd number, Alice cannot perform the global halving move, so the answer is $\sum_{i=1}^{n} \bigl(\text{popcnt}(a_i) + \lfloor \log_2 a_i \rfloor + 1 - 1\bigr) = \sum_{i=1}^{n} \text{popcnt}(a_i) + |a_i| - 1$, where $|a_i|$ denotes the bit-length of $a_i$. If every number is even, Alice can keep halving globally until some value becomes odd, and after that the cost above still applies.&lt;/p&gt;
&lt;p&gt;This suggests fixing the number of global halvings we perform as $j$, then computing the minimum number of &lt;code&gt;+1&lt;/code&gt; operations required to make every $a_i$ a multiple of $2^j$. A naive choice is $\text{step} = 2^j - (a_i \bmod 2^j)$, but that isn&apos;t always optimal — sometimes a few extra &lt;code&gt;+1&lt;/code&gt;s can eliminate more $1$-bits from the binary representation. So we need to search a small window around $a_i$.&lt;/p&gt;
&lt;p&gt;How wide should the window be? Notice that $\text{popcnt}(a_i) + |a_i| - 1$ is bounded by $17 + 17 - 1 = 33$ for $a_i \le 10^5$. That means increasing $a_i$ by more than $33$ is never worthwhile — the extra &lt;code&gt;+1&lt;/code&gt;s would already exceed the entire remaining cost. So for every element we only need to try the $33$ values immediately above $a_i$, keep those divisible by $2^j$, and pick the minimum. Total complexity: $O(n \log^2)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define endl &apos;\n&apos;
#define fi first
#define se second
#define ll long long
#define lowbit(x) (x&amp;amp;(-x))
const int mod=998244353;
const double eps=1e-12;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
#define popcnt __builtin_popcount
int dcmp(double x){if(fabs(x)&amp;lt;eps)return 0;return x&amp;gt;0?1:-1;}

#define int ll

// mt19937 rnd(random_device{}());
// uniform_int_distribution&amp;lt;int&amp;gt;dist(0,1000000);

int a[100005];
int b[100005];
int jie[21];

int len(int x)
{
    int cnt=0;
    while(x)
    {
        cnt++;
        x&amp;gt;&amp;gt;=1;
    }
    return cnt;
}

void solve()
{
    int n;
    cin&amp;gt;&amp;gt;n;
    int ans=inf;
    for(int i=1;i&amp;lt;=n;i++)
    {
        cin&amp;gt;&amp;gt;a[i];
    }
    for(int j=0;j&amp;lt;=20;j++)
    {
        int res=j;
        for(int i=1;i&amp;lt;=n;i++)
        {
            int tmp=inf;
            for(int k=a[i];k&amp;lt;=a[i]+33;k++)
            {
                if(k%jie[j]==0)
                {
                    b[i]=k/jie[j];
                    tmp=min(tmp,k-a[i]+popcnt(b[i])+len(b[i])-1);
                }
            }
            b[i]=a[i]+jie[j]-a[i]%jie[j];
            tmp=min(tmp,b[i]-a[i]+popcnt(b[i]/jie[j])+len(b[i]/jie[j])-1);
            res+=tmp;
        }
        ans=min(ans,res);
    }
    cout&amp;lt;&amp;lt;ans&amp;lt;&amp;lt;&apos;\n&apos;;
}

/*
 110
1000
  10
 100
   1
*/

#undef int

int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    // cout&amp;lt;&amp;lt;fixed&amp;lt;&amp;lt;setprecision(10);

    jie[0]=1;
    for(int i=1;i&amp;lt;=20;i++)
    {
        jie[i]=jie[i-1]*2;
    }

    int _;cin&amp;gt;&amp;gt;_;while(_--)
    {
        solve();
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>diss_quack and Array Game (CF 2246 D)</title><link>https://jerryblack.vercel.app/posts/diss-quack-and-array-game/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/diss-quack-and-array-game/</guid><description>博弈 + 位运算枚举，通过枚举全体除二的次数来计算最少步数。</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/2246/problem/D&quot;&gt;Codeforces Round 1108 (Div. 2) D&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;题目大意&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;有一串长度为 $n$ 的数列 $a\ (0\leq a_i\leq 10^5)$，Alice 在游戏前可以对任意一个位置上的数进行任意次   的操作，每操作一次记为一步。游戏开始后 Alice 和 Bob 轮流操作，Bob 先手：选择两个位置并交换（可以选同一个位置）。Alice 后手：若 $a_1$ 为偶数，则选择最大的 $j$，使得 $a_i\pmod 2=0,\forall i\leq j$，并将 $a_i:=a_i/2,\forall i\le j$，记为一步；否则，将 $a_1:=a_1-1$，记为一步。如果有数变为零，则将该数从数组中移除。Bob 想让 Alice 的步数越多越好，Alice 想让自己的步数越少越好，问 Alice 最少步数是多少。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;思路&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;先考虑没有 &lt;code&gt;+1&lt;/code&gt; 操作的情形，若当前有奇数，则 Alice 无法进行 &lt;code&gt;/2&lt;/code&gt; 的操作，因此答案为 $\sum_{i=1}^npopcnt(a_i)+|a_i|-1$。若当前没有偶数，则 Alice 可以一直进行全体 &lt;code&gt;/2&lt;/code&gt; 的操作，一直到出现奇数，接下来所需要的操作数同上。因此，我们可以钦定需要做几次全体 &lt;code&gt;/2&lt;/code&gt; 操作为 $j$，则需要计算将 $a_i$ 加成 $2^j$ 的倍数所需要的步数。一个想当然的做法是 $step=2^j-(a_i \pmod {2^j})$，这个当然没问题，但不一定是最优的，有时候多进行几次 &lt;code&gt;+1&lt;/code&gt; 的操作可以使二进制里少去更多的 $1$，所以我们还需要遍历一个范围，而这个范围怎么求呢？我们再来看 $popcnt(a_i)+|a_i|-1$ 这个式子，对于每一个位置的最多操作数也就是 $17+17-1=33$。所以对于上述情况，我们只需要额外遍历 $33$ 个数，找到其中的 $2^j$ 的倍数，并在里面求最小步数即可，复杂度为 $O(n\log^2)$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define endl &apos;\n&apos;
#define fi first
#define se second
#define ll long long
#define lowbit(x) (x&amp;amp;(-x))
const int mod=998244353;
const double eps=1e-12;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
#define popcnt __builtin_popcount
int dcmp(double x){if(fabs(x)&amp;lt;eps)return 0;return x&amp;gt;0?1:-1;}

#define int ll

// mt19937 rnd(random_device{}());
// uniform_int_distribution&amp;lt;int&amp;gt;dist(0,1000000);

int a[100005];
int b[100005];
int jie[21];

int len(int x)
{
    int cnt=0;
    while(x)
    {
        cnt++;
        x&amp;gt;&amp;gt;=1;
    }
    return cnt;
}

void solve()
{
    int n;
    cin&amp;gt;&amp;gt;n;
    int ans=inf;
    for(int i=1;i&amp;lt;=n;i++)
    {
        cin&amp;gt;&amp;gt;a[i];
    }
    for(int j=0;j&amp;lt;=20;j++)
    {
        int res=j;
        for(int i=1;i&amp;lt;=n;i++)
        {
            int tmp=inf;
            for(int k=a[i];k&amp;lt;=a[i]+33;k++)
            {
                if(k%jie[j]==0)
                {
                    b[i]=k/jie[j];
                    tmp=min(tmp,k-a[i]+popcnt(b[i])+len(b[i])-1);
                }
            }
            b[i]=a[i]+jie[j]-a[i]%jie[j];
            tmp=min(tmp,b[i]-a[i]+popcnt(b[i]/jie[j])+len(b[i]/jie[j])-1);
            res+=tmp;
        }
        ans=min(ans,res);
    }
    cout&amp;lt;&amp;lt;ans&amp;lt;&amp;lt;&apos;\n&apos;;
}

/*
 110
1000
  10
 100
   1
*/

#undef int

int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    // cout&amp;lt;&amp;lt;fixed&amp;lt;&amp;lt;setprecision(10);

    jie[0]=1;
    for(int i=1;i&amp;lt;=20;i++)
    {
        jie[i]=jie[i-1]*2;
    }

    int _;cin&amp;gt;&amp;gt;_;while(_--)
    {
        solve();
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Interval (2020 Nowcoder Multi-School #5)</title><link>https://jerryblack.vercel.app/posts/interval-nowcoder-en/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/interval-nowcoder-en/</guid><description>Persistent segment tree + segment tree for range AND set-size queries with forced online mode.</description><pubDate>Thu, 04 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://ac.nowcoder.com/acm/contest/55996/H&quot;&gt;Problem link&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;You are given an array $A$ of length $N$ with $0 \le A_i &amp;lt; 2^{30}$ and $1 \le N \le 10^5$.&lt;/p&gt;
&lt;p&gt;Define $F(l, r) := A_l ,&amp;amp;, A_{l+1} ,&amp;amp;, \cdots ,&amp;amp;, A_r$ (bitwise AND over the range).&lt;/p&gt;
&lt;p&gt;Define $S(l, r) := {, F(a, b) \mid \min(l, r) \le a \le b \le \max(l, r) ,}$.&lt;/p&gt;
&lt;p&gt;There are $Q$ queries with $1 \le Q \le 10^5$. For each query $(L, R)$ with $1 \le L, R \le N$, output $|S(L, R)|$ — the number of distinct values that appear as an AND of any subarray inside $[L, R]$. Queries are forced-online.&lt;/p&gt;
&lt;h2&gt;Approach&lt;/h2&gt;
&lt;p&gt;Fix the right endpoint and slide the left endpoint leftward. Because a bitwise AND only ever loses bits, $F(l, r)$ takes at most $O(\log V)$ distinct values, and each transition can be found via binary search on a segment tree that supports range AND.&lt;/p&gt;
&lt;p&gt;Since queries are online, we need every historical state, so we maintain a &lt;strong&gt;persistent segment tree&lt;/strong&gt;: version $i$&apos;s tree records, at position $j$, whether $F(j, i)$ contributes to the answer. Deduplication uses a classic trick — for each value only keep the &lt;em&gt;latest&lt;/em&gt; position that produces it, deleting the previous occurrence. This ensures each distinct AND value is counted exactly once.&lt;/p&gt;
&lt;p&gt;The problem then reduces to: point update, range sum query on the persistent segment tree.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define ll long long
#define fi first
#define se second
const double eps=1e-12;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1.0);
int dcmp(double x){if(fabs(x)&amp;lt;eps)return 0;return x&amp;gt;0?1:-1;}

#define int ll

struct president_segment_tree
{
    int cnt=0;
    int root[100005];
    struct node
    {
        int l,r,sum;
    }z[100005*600];
    int clone(int x)
    {
        cnt++;z[cnt]=z[x];
        return cnt;
    }
    void update(int id1,int &amp;amp;id2,int l,int r,int x,int w)
    {
        id2=clone(id1);
        z[id2].sum+=w;
        if(l==r)return;
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            if(x&amp;lt;=mid)update(z[id1].l,z[id2].l,l,mid,x,w);
            else update(z[id1].r,z[id2].r,mid+1,r,x,w);
        }
    }
    int query(int id,int l,int r,int x,int y)
    {
        if(x&amp;lt;=l&amp;amp;&amp;amp;r&amp;lt;=y)return z[id].sum;
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            int ans=0;
            if(x&amp;lt;=mid)ans+=query(z[id].l,l,mid,x,y);
            if(mid&amp;lt;y)ans+=query(z[id].r,mid+1,r,x,y);
            return ans;
        }
    }
}pst;
int a[100005];
struct segment_tree
{
    int tree[100005&amp;lt;&amp;lt;2];
    void build(int p,int l,int r)
    {
        if(l==r)tree[p]=a[l];
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            build(p&amp;lt;&amp;lt;1,l,mid);
            build(p&amp;lt;&amp;lt;1|1,mid+1,r);
            tree[p]=tree[p&amp;lt;&amp;lt;1]&amp;amp;tree[p&amp;lt;&amp;lt;1|1];
        }
    }
    int query(int p,int l,int r,int x,int y)
    {
        if(x&amp;lt;=l&amp;amp;&amp;amp;r&amp;lt;=y)return tree[p];
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            int ans=(1&amp;lt;&amp;lt;30)-1;
            if(x&amp;lt;=mid)ans&amp;amp;=query(p&amp;lt;&amp;lt;1,l,mid,x,y);
            if(mid&amp;lt;y)ans&amp;amp;=query(p&amp;lt;&amp;lt;1|1,mid+1,r,x,y);
            return ans;
        }
    }
}st;
map&amp;lt;int,int&amp;gt;last;

void solve()
{
    int n;
    cin&amp;gt;&amp;gt;n;
    for(int i=1;i&amp;lt;=n;i++)cin&amp;gt;&amp;gt;a[i];
    st.build(1,1,n);
    for(int i=1;i&amp;lt;=n;i++)
    {
        pst.root[i]=pst.root[i-1];
        if(last.count(a[i]))
            pst.update(pst.root[i],pst.root[i],1,n,last[a[i]],-1);
        last[a[i]]=i;
        pst.update(pst.root[i],pst.root[i],1,n,last[a[i]],1);
        int cur=a[i];
        while(true)
        {
            int l=0,r=i,res=0;
            while(l&amp;lt;r)
            {
                int mid=(l+r+1)&amp;gt;&amp;gt;1;
                int now=st.query(1,1,n,mid,i);
                if(now&amp;lt;cur)l=mid,res=mid;
                else r=mid-1;
            }
            if(!res)break;
            cur=st.query(1,1,n,res,i);
            if(last.count(cur))
                pst.update(pst.root[i],pst.root[i],1,n,last[cur],-1);
            last[cur]=res;
            pst.update(pst.root[i],pst.root[i],1,n,last[cur],1);
        }
    }
    int q;
    cin&amp;gt;&amp;gt;q;
    int lastans=0;
    while(q--)
    {
        int l,r;
        cin&amp;gt;&amp;gt;l&amp;gt;&amp;gt;r;
        l=(l^lastans)%n+1;
        r=(r^lastans)%n+1;
        if(l&amp;gt;r)swap(l,r);
        cout&amp;lt;&amp;lt;(lastans=pst.query(pst.root[r],1,n,l,n))&amp;lt;&amp;lt;&apos;\n&apos;;
    }
}

#undef int

int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    {
        solve();
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Interval（20年牛客多校5）</title><link>https://jerryblack.vercel.app/posts/interval-nowcoder/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/interval-nowcoder/</guid><description>主席树 + 线段树维护区间与操作，处理强制在线的区间子集大小查询。</description><pubDate>Thu, 04 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://ac.nowcoder.com/acm/contest/55996/H&quot;&gt;传送门&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;题意&lt;/h2&gt;
&lt;p&gt;有一个数列 $A(0\leqslant A_i\lt2^{30})$ 长度为 $N(1\leqslant N\leqslant10^5)$，&lt;/p&gt;
&lt;p&gt;$F(l,r):=A_l&amp;amp;A_{l+1}&amp;amp;\cdots&amp;amp;A_r$，&lt;/p&gt;
&lt;p&gt;$S(l,r):=\left{F(a,b)|\min(l,r)\leqslant a\leqslant b\leqslant\max(l,r)\right}$，&lt;/p&gt;
&lt;p&gt;有 $Q(1\leqslant Q\leqslant10^5)$ 组询问，对于给定的 $L,R(1\leqslant L&apos;,R&apos;\leqslant N)$，求 $S(L,R)$ 的大小，强制在线。&lt;/p&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;考虑到固定一个右端点，左端点向左拓展的时候，$F(l,r)$ 的个数不会很多，最多只有 $\log$ 个，于是我们就可以用二分预处理出所有的值。由于需要保留上一个版本的答案，所以我们需要拿一颗主席树来维护，第 $i$ 个版本的线段树中第 $j$ 个位置记录的是 $F(j,i)$ 是否对答案有贡献，这样就涉及了去重的这个问题。一个常见的技巧是对于一个值，只记录离第 $i$ 个位置最近的那个位置，并将之前的位置删去，这样就可以保证一个值只被算一次了，所以这道题就被转换成了主席树上单点修改，区间查询。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define ll long long
#define fi first
#define se second
const double eps=1e-12;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1.0);
int dcmp(double x){if(fabs(x)&amp;lt;eps)return 0;return x&amp;gt;0?1:-1;}

#define int ll

struct president_segment_tree
{
    int cnt=0;
    int root[100005];
    struct node
    {
        int l,r,sum;
    }z[100005*600];
    int clone(int x)
    {
        cnt++;z[cnt]=z[x];
        return cnt;
    }
    void update(int id1,int &amp;amp;id2,int l,int r,int x,int w)
    {
        id2=clone(id1);
        z[id2].sum+=w;
        if(l==r)return;
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            if(x&amp;lt;=mid)update(z[id1].l,z[id2].l,l,mid,x,w);
            else update(z[id1].r,z[id2].r,mid+1,r,x,w);
        }
    }
    int query(int id,int l,int r,int x,int y)
    {
        if(x&amp;lt;=l&amp;amp;&amp;amp;r&amp;lt;=y)return z[id].sum;
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            int ans=0;
            if(x&amp;lt;=mid)ans+=query(z[id].l,l,mid,x,y);
            if(mid&amp;lt;y)ans+=query(z[id].r,mid+1,r,x,y);
            return ans;
        }
    }
}pst;
int a[100005];
struct segment_tree
{
    int tree[100005&amp;lt;&amp;lt;2];
    void build(int p,int l,int r)
    {
        if(l==r)tree[p]=a[l];
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            build(p&amp;lt;&amp;lt;1,l,mid);
            build(p&amp;lt;&amp;lt;1|1,mid+1,r);
            tree[p]=tree[p&amp;lt;&amp;lt;1]&amp;amp;tree[p&amp;lt;&amp;lt;1|1];
        }
    }
    int query(int p,int l,int r,int x,int y)
    {
        if(x&amp;lt;=l&amp;amp;&amp;amp;r&amp;lt;=y)return tree[p];
        else
        {
            int mid=(l+r)&amp;gt;&amp;gt;1;
            int ans=(1&amp;lt;&amp;lt;30)-1;
            if(x&amp;lt;=mid)ans&amp;amp;=query(p&amp;lt;&amp;lt;1,l,mid,x,y);
            if(mid&amp;lt;y)ans&amp;amp;=query(p&amp;lt;&amp;lt;1|1,mid+1,r,x,y);
            return ans;
        }
    }
}st;
map&amp;lt;int,int&amp;gt;last;

void solve()
{
    int n;
    cin&amp;gt;&amp;gt;n;
    for(int i=1;i&amp;lt;=n;i++)cin&amp;gt;&amp;gt;a[i];
    st.build(1,1,n);
    for(int i=1;i&amp;lt;=n;i++)
    {
        pst.root[i]=pst.root[i-1];
        if(last.count(a[i]))
            pst.update(pst.root[i],pst.root[i],1,n,last[a[i]],-1);
        last[a[i]]=i;
        pst.update(pst.root[i],pst.root[i],1,n,last[a[i]],1);
        int cur=a[i];
        while(true)
        {
            int l=0,r=i,res=0;
            while(l&amp;lt;r)
            {
                int mid=(l+r+1)&amp;gt;&amp;gt;1;
                int now=st.query(1,1,n,mid,i);
                if(now&amp;lt;cur)l=mid,res=mid;
                else r=mid-1;
            }
            if(!res)break;
            cur=st.query(1,1,n,res,i);
            if(last.count(cur))
                pst.update(pst.root[i],pst.root[i],1,n,last[cur],-1);
            last[cur]=res;
            pst.update(pst.root[i],pst.root[i],1,n,last[cur],1);
        }
    }
    int q;
    cin&amp;gt;&amp;gt;q;
    int lastans=0;
    while(q--)
    {
        int l,r;
        cin&amp;gt;&amp;gt;l&amp;gt;&amp;gt;r;
        l=(l^lastans)%n+1;
        r=(r^lastans)%n+1;
        if(l&amp;gt;r)swap(l,r);
        cout&amp;lt;&amp;lt;(lastans=pst.query(pst.root[r],1,n,l,n))&amp;lt;&amp;lt;&apos;\n&apos;;
    }
}

#undef int

int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    {
        solve();
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Classic Problems on Suffix Arrays</title><link>https://jerryblack.vercel.app/posts/suffix-array-en/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/suffix-array-en/</guid><description>Working through the canonical suffix-array problems from Luo Suiqian&apos;s 2009 IOI national team paper, using the SAIS template.</description><pubDate>Tue, 23 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;It&apos;s been a while since I last wrote a post. I recently spent some time on suffix arrays and learned a lot, so I wanted to write this up as a keepsake. Credit where it&apos;s due — the template used below is YZH&apos;s SAIS implementation, and the section titles and example problems come from Luo Suiqian&apos;s 2009 IOI national team paper. Respect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Problems on a Single String&lt;/h2&gt;
&lt;h3&gt;Longest Non-Overlapping Repeated Substring&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Sort all suffixes, then binary-search the answer. For a candidate length $l$, group the sorted suffixes so that every group has &lt;code&gt;lcp(height)&lt;/code&gt; at least $l$. Within each group, track the maximum and minimum of &lt;code&gt;rl(sa)&lt;/code&gt;; if their difference exceeds $l$, a non-overlapping repeated substring of length $l$ exists. This is $O(n \log n)$. You can also do it in $O(n)$ by using two pointers together with a monotonic deque to maintain the max/min of &lt;code&gt;rl(sa)&lt;/code&gt;; the answer is the largest &lt;code&gt;lcp(height)&lt;/code&gt; seen across valid groups.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=1743&quot;&gt;Musical Theme&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/7vCDYMSX&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Longest Repeated Substring Occurring at Least $k$ Times (Overlapping Allowed)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The same binary-search-on-$l$ approach works: check whether some group with &lt;code&gt;lcp(height) &amp;gt;= l&lt;/code&gt; contains at least $k$ suffixes. That&apos;s $O(n \log n)$. It also runs in $O(n)$ with two pointers by sliding a window of exactly $k$ consecutive suffixes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3261&quot;&gt;Milk Patterns&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/4BmKckDM&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Number of Distinct Substrings&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice that for the suffix ranked $i$-th, all substrings starting at &lt;code&gt;rl[i]&lt;/code&gt; and ending at or before &lt;code&gt;lcp[i]&lt;/code&gt; characters into the suffix would be double-counted. Subtracting those out, the answer is
$$\sum_{i=1}^{n} \bigl(n - rl[i] + 1 - lcp[i]\bigr),$$
computable in $O(n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/DISUBSTR/&quot;&gt;DISUBSTR - Distinct Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/WZPwe5H7&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Longest Palindromic Substring&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Reverse the string, concatenate it to the original with an unused separator in the middle, then run suffix array on the combined string. Enumerate center points (splitting into odd/even-length cases) and combine with $ST$-table range-min on the height array. Precomputation is $O(n \log n)$; querying is $O(1)$, so total $O(n \log n)$. If you replace the $ST$ table with Cartesian tree + Tarjan for $O(1)$ RMQ with $O(n)$ preprocessing, the whole solution becomes $O(n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://acm.timus.ru/problem.aspx?space=1&amp;amp;num=1297&quot;&gt;Palindrome&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/xDD2xktW&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Continuous Repetition Detection&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Observation: if a string is the concatenation of $k$ copies of a period of length $l$, then $lcp(s[1], s[l+1]) = n - l$. Enumerate the divisors of $n$ and use the $ST$-table to check the $lcp$; that&apos;s $O(n \log n)$. If you notice that one endpoint of the RMQ is always fixed, you can precompute in $O(n)$ and drop the $\log$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=2406&quot;&gt;Power Strings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Yx6ZTHhn&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Substring with Maximum Number of Continuous Repetitions&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Enumerate period length $l$. For any candidate period, positions $s[1], s[1+l], \dots, s[1+xl]$ must fall inside a run of length-$l$ repeats. For adjacent samples $s[1 + al]$ and $s[1 + al + l]$, their $lcp$ gives the maximum stretch to the right; if $lcp$ isn&apos;t a multiple of $l$, check whether pushing the start one step to the left adds one more copy. The complexity works out to
$$O!\left(\sum_{i=1}^{n} \frac{n}{i}\right) = O(n \log n).$$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3693&quot;&gt;Maximum repetition substring&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/qEbUcPAX&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/REPEATS/&quot;&gt;REPEATS - Repeats&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/wrWFSQn6&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Problems on Two Strings&lt;/h2&gt;
&lt;h3&gt;Longest Common Substring&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Concatenate the two strings with an unused separator, then run suffix array on the combined string. The longest common substring must appear as the $lcp$ of two adjacent suffixes that come from different original strings. $O(n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=2774&quot;&gt;Long Long Message&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/4AJwWmjh&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://acm.timus.ru/problem.aspx?space=1&amp;amp;num=1517&quot;&gt;Freedom of Choice&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Uqb1Lekx&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Count of Common Substrings of Length at Least $k$&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Concatenate as above and run suffix array. For each suffix of $B$, count contributions from previously seen suffixes of $A$, then swap roles and do the same. A monotonic stack keeps the running sum in $O(1)$ amortized per suffix, giving $O(n)$ overall.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3415&quot;&gt;Common Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Z08tqH5b&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Problems on Multiple Strings&lt;/h2&gt;
&lt;h3&gt;Longest Substring Appearing in at Least $k$ Strings&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Concatenate all strings using $k$ &lt;em&gt;distinct&lt;/em&gt; unused separators, then run suffix array. Binary-search the length $l$: for each contiguous group with &lt;code&gt;lcp &amp;gt;= l&lt;/code&gt;, check whether the group spans at least $k$ different source strings. $O(n \log n)$. As before, two pointers + a monotonic deque bring this down to $O(n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3294&quot;&gt;Life Forms&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/yyJu0ir0&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Longest Substring Appearing at Least Twice Non-Overlapping in Every String&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Same concatenation trick, then binary-search $l$. For each &lt;code&gt;lcp &amp;gt;= l&lt;/code&gt; group, per source string track the max and min &lt;code&gt;rl&lt;/code&gt; and check that every string has two occurrences that don&apos;t overlap. $O(n \log n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/PHRASES/&quot;&gt;PHRASES - Relevant Phrases of Annihilation&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/YWsAxG6D&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Longest Substring Appearing (or Reverse-Appearing) in Every String&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;For every string, append its reverse and use $2n$ distinct unused separators between the pieces. Run suffix array on the combined string, then binary-search $l$ and check whether each group covers every original string. Also $O(n \log n)$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=1226&quot;&gt;Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/GP2LEwDB&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>后缀数组基础题套路赏析</title><link>https://jerryblack.vercel.app/posts/suffix-array/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/suffix-array/</guid><description>基于罗穗骞2009年国集论文，使用SAIS模板，整理后缀数组经典题型与解题套路。</description><pubDate>Tue, 23 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;好久没写博客了，最近学了一下后缀数组，感觉收获颇丰，于是想写一篇博客纪念一下。特此申明一下，这里用的板子是YZH大佬的SAIS模板，然后小标题和题目用的是罗穗骞大佬2009年国集论文中的标题和例题，%%%。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;单个字符串的相关问题&lt;/h2&gt;
&lt;h3&gt;不可重叠最长重复子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;首先当然是对所有后缀进行排序，然后我们考虑二分答案，假设当前的长度是 $l$，那么我们可以把排好序的后缀分成几段，其中每一段的&lt;code&gt;lcp(height)&lt;/code&gt;都是大于等于$l$的，于是我们记录一下每一段后缀中&lt;code&gt;rl(sa)&lt;/code&gt;的最大值和最小值，如果最大值和最小值的差大于$l$，那么就说明有不重叠的重复子串，反之则不然，这样的做法是$O(n\log n)$的。当然，我们还可以有$O(n)$的做法，在尺取的同时，用单调队列维护一下&lt;code&gt;rl(sa)&lt;/code&gt;的最大值和最小值，并且答案就是所有符合条件的&lt;code&gt;lcp(height)&lt;/code&gt;的最大值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=1743&quot;&gt;Musical Theme&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/7vCDYMSX&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;可重叠的k次最长重复子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;首先可以用同上题的做法，二分答案$l$，对于&lt;code&gt;lcp(height)&lt;/code&gt;大于等于$l$的每一段，看看是否长度超过$k$，同样，这个方法的复杂度是$O(n\log n)$的。但是我们可以发现，用尺取，每次取长度为$k$的一段，同样可以把复杂度优化到$O(n)$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3261&quot;&gt;Milk Patterns&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/4BmKckDM&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;子串的个数&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们可以发现，对于后缀排序是$i$的后缀，以&lt;code&gt;rl[i](sa[i])&lt;/code&gt;作为开头，并且以&lt;code&gt;lcp[i](height[i])&lt;/code&gt;及其之前最为结尾的所有子串都会被重复计算，于是我们就不去记这一段对答案的贡献，于是我们所要求的答案就是$\displaystyle\sum_{i=1}^nn-rl[i]+1-lcp[i]$，显然这样的做法的复杂度是$O(n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/DISUBSTR/&quot;&gt;DISUBSTR - Distinct Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/WZPwe5H7&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;最长回文子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们考虑把一整个字符串复制一段并且翻转，接在原字符串的后面，中间用一个没出现过的字符连接，然后我们对这个新的字符串进行后缀排序，于是我们可以通过枚举中心节点来更新答案，其中中心节点需要根据长度的奇偶性来分类枚举，由于$ST$表预处理的复杂度是$O(n\log n)$的，所以这个做法的整体复杂度也是$O(n\log n)$的。当然，如果能够把$RMQ$的复杂度降为$O(1)$，那么就可以把整体复杂度降到$O(n)$，具体的话就是笛卡尔树＋$Tarjan$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://acm.timus.ru/problem.aspx?space=1&amp;amp;num=1297&quot;&gt;Palindrome&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/xDD2xktW&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;连续重复子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;通过观察可以发现，如果一个串的连续重复子串的长度为$l$，那么有串$s[1]$和$s[l+1]$的$lcp$等于$n-l$，所以就只需要枚举一下总长度的因子就行，然后$lcp$可以用$ST$表求得，这样做的复杂度是$O(n\log n)$的。当然还能进行优化，由于$RMQ$的一端是固定的，于是可以用一个数组$O(n)$处理，这样就可以把复杂度降到$O(n)$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=2406&quot;&gt;Power Strings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Yx6ZTHhn&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;重复次数最多的连续重复子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们考虑枚举长度$l$为重复周期，那么就可以发现$s[1],s[1+l],\cdots,s[1+x\times l]$一定会在一个连续重复子串中，于是对于$s[1+a\times l]$和$s[1+a\times l+l]$，他们的$lcp$就是最长能往后延伸的长度，如果$lcp$不能被$l$整除，就看看能否往前再延伸一小段距离使得当前的答案增加一，然后算一下复杂度是$\displaystyle O\left(\sum_{i=1}^n{n\over i}\right)$即$O(n\log n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3693&quot;&gt;Maximum repetition substring&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/qEbUcPAX&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/REPEATS/&quot;&gt;REPEATS - Repeats&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/wrWFSQn6&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;两个字符串的相关问题&lt;/h2&gt;
&lt;h3&gt;最长公共子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们考虑把两段用一个没出想过的字符连起来，把这个新的字符串进行后缀排序。可以发现，最长公共子串一定出现在相邻两串的$lcp$中，这样的复杂度是$O(n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=2774&quot;&gt;Long Long Message&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/4AJwWmjh&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://acm.timus.ru/problem.aspx?space=1&amp;amp;num=1517&quot;&gt;Freedom of Choice&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Uqb1Lekx&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;长度不小于k的公共子串的个数&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们先按之前的方法连接字符串并且进行后缀排序，然后对于两个位于不同串的后缀，他们对答案的贡献很好处理，但是有这么多后缀，于是考虑怎么优化。我们首先对于每一个串$B$的后缀，计算它与它之前出现过的串$A$的后缀对答案的贡献，然后对串$A$也是同样，同时我们可以用一个单调栈进行优化，这样的复杂度是$O(n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3415&quot;&gt;Common Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/Z08tqH5b&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;多个字符串的相关问题&lt;/h2&gt;
&lt;h3&gt;出现在不小于k个字符串中的最长子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;我们先把所有串都连在一起，并且中间用没出现过的并且互不相同的字符相连，然后对新串进行后缀排序，然后同样我们可以二分长度$l$，对于&lt;code&gt;lcp&lt;/code&gt;大于等于$l$的每一段，看是否出现在不少于$k$个串中，这样的复杂度是$O(n\log n)$的，当然可以参照之前的方法，用尺取＋单调队列把复杂度优化到$O(n)$。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=3294&quot;&gt;Life Forms&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/yyJu0ir0&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;在每个串中都至少出现两次且不重叠的最长子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;先像之前那样构造新串并进行后缀排序，然后二分答案$l$，对于&lt;code&gt;lcp&lt;/code&gt;大于等于$l$的每一段，记录在每个串中&lt;code&gt;rl&lt;/code&gt;的最大值和最小值，并进行判断即可，这样的复杂度是$O(n\log n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.spoj.com/problems/PHRASES/&quot;&gt;PHRASES - Relevant Phrases of Annihilation&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/YWsAxG6D&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;出现或反转后出现在每个字符串中的最长子串&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;把每个串都复制一遍并翻转然后用$2\times n$个没出现过的且互不相同的字符连接这些字符串，再把新串进行后缀排序，二分长度$l$，对于&lt;code&gt;lcp&lt;/code&gt;大于等于$l$的每一段，看是否在每个串中都出现过，这样的复杂度同样也是$O(n\log n)$的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&lt;a href=&quot;http://poj.org/problem?id=1226&quot;&gt;Substrings&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://pastebin.com/GP2LEwDB&quot;&gt;code&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Cards (ABC 247 F)</title><link>https://jerryblack.vercel.app/posts/cards-abc247f-en/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/cards-abc247f-en/</guid><description>Lucas numbers + Union-Find, solving a cycle-cover counting problem.</description><pubDate>Fri, 29 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://vjudge.net/problem/AtCoder-abc247_f/origin&quot;&gt;AtCoder - abc247_f&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Problem Summary&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;You have $N$ cards. Each card has two sides — the front shows a number $P_i$ and the back shows $Q_i$. Both sequences $P$ and $Q$ are permutations of $(1, 2, \dots, N)$. Count the number of subsets of cards such that every number from $1$ to $N$ appears on at least one chosen card. Output the answer modulo $998244353$. $(1 \le N \le 2 \times 10^5)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Approach&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Build a graph by connecting the two numbers on each card. Since $P$ and $Q$ are permutations, every value appears exactly twice, so every vertex has degree $2$ and the resulting graph is a disjoint union of cycles.&lt;/p&gt;
&lt;p&gt;Consider a simpler sub-problem first: on a &lt;em&gt;chain&lt;/em&gt; of $m$ numbers $1, 2, \dots, m$, count the ways to pick a subset such that for every pair of adjacent numbers at least one is chosen. Splitting by whether $m$ is picked, let $f(m)$ denote the answer. We have $f(1) = 2$, $f(2) = 3$, and $f(m) = f(m-1) + f(m-2)$ — Fibonacci-like.&lt;/p&gt;
&lt;p&gt;Now the real sub-problem: on a &lt;em&gt;cycle&lt;/em&gt; of $m$ numbers, count the subsets where for every vertex at least one of its two incident edges is chosen. Let $g(m)$ denote the answer. Casework on whether the edge $(1, m)$ is picked gives $g(1) = 1$, $g(2) = 3$, and $g(m) = f(m-1) + f(m-3)$. A quick check reveals $g(m) = L_m$, the $m$-th Lucas number.&lt;/p&gt;
&lt;p&gt;Multiply $g(\text{cycle length})$ across every cycle in the graph to get the final answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
long long a[200005];
long long mod=998244353;
int p[200005];
int q[200005];
int fa[200005];
int siz[200005];
int vis[200005];
int findd(int x)
{
    return fa[x]==x?x:(fa[x]=findd(fa[x]));
}
int main()
{
    int n;
    scanf(&quot;%d&quot;,&amp;amp;n);
    for(int i=1;i&amp;lt;=n;i++)scanf(&quot;%d&quot;,&amp;amp;p[i]);
    for(int i=1;i&amp;lt;=n;i++)scanf(&quot;%d&quot;,&amp;amp;q[i]);
    for(int i=1;i&amp;lt;=n;i++){fa[i]=i;siz[i]=1;}
    for(int i=1;i&amp;lt;=n;i++)if(findd(p[i])!=findd(q[i]))
    {
        siz[findd(q[i])]+=siz[findd(p[i])];
        fa[findd(p[i])]=findd(q[i]);
    }
    a[0]=2;
    a[1]=1;
    for(int i=2;i&amp;lt;=n;i++)a[i]=(a[i-1]+a[i-2])%mod;
    long long ans=1;
    for(int i=1;i&amp;lt;=n;i++)if(!vis[findd(i)])
    {
        ans=ans*a[siz[findd(i)]]%mod;
        vis[findd(i)]=1;
    }
    printf(&quot;%lld\n&quot;,ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Cards (ABC 247 F)</title><link>https://jerryblack.vercel.app/posts/cards-abc247f/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/cards-abc247f/</guid><description>卢卡斯数列 + 并查集，处理环上覆盖计数问题。</description><pubDate>Fri, 29 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://vjudge.net/problem/AtCoder-abc247_f/origin&quot;&gt;AtCoder - abc247_f&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;题目大意&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;你有$N$张卡片，每张卡有正反两面，正面有一个数字$P_i$，反面有一个数字$Q_i$，数列$P$和$Q$都是$(1,2,\dots,N)$的全排列，问有多少种选择方法使得$N$个数都至少一次出现在被选中的牌上，答案对$998244353$取模。$(1\le N\le2\times 10^5)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;思路&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;首先，我们建一张图，把每张牌正反的两个数连起来，而由于$P$和$Q$是全排列，每个数都只会出现两次，每个点的度都是$2$，所以我们建出来的图会由很多个环构成。然后我们先来考虑一个简单一点的问题：$1,2,\dots,m$这$m$个数中相邻的两个数至少得选一个的方案数是多少。就是分类讨论$m$是否选，这样的话，设答案为$f(m)$，则满足$f(1)=2,f(2)=3,\dots,f(m)=f(m-1)+f(m-2)$。然后现在我们要求的是$1,2,\dots,m$这$m$个数围成一个圈时连着一个点的两条边至少得选一条的方案数，设答案为$g(m)$，分类讨论一下$1$和$m$是否相连，就能得到$g(1)=1,g(2)=3,\dots,g(m)=f(m-1)+f(m-3)$，观察一下可以发现$g(m)=L_m$，$L$为卢卡斯数列。这样，我们只要把每个环的答案乘起来就结束了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
long long a[200005];
long long mod=998244353;
int p[200005];
int q[200005];
int fa[200005];
int siz[200005];
int vis[200005];
int findd(int x)
{
    return fa[x]==x?x:(fa[x]=findd(fa[x]));
}
int main()
{
    int n;
    scanf(&quot;%d&quot;,&amp;amp;n);
    for(int i=1;i&amp;lt;=n;i++)scanf(&quot;%d&quot;,&amp;amp;p[i]);
    for(int i=1;i&amp;lt;=n;i++)scanf(&quot;%d&quot;,&amp;amp;q[i]);
    for(int i=1;i&amp;lt;=n;i++){fa[i]=i;siz[i]=1;}
    for(int i=1;i&amp;lt;=n;i++)if(findd(p[i])!=findd(q[i]))
    {
        siz[findd(q[i])]+=siz[findd(p[i])];
        fa[findd(p[i])]=findd(q[i]);
    }
    a[0]=2;
    a[1]=1;
    for(int i=2;i&amp;lt;=n;i++)a[i]=(a[i-1]+a[i-2])%mod;
    long long ans=1;
    for(int i=1;i&amp;lt;=n;i++)if(!vis[findd(i)])
    {
        ans=ans*a[siz[findd(i)]]%mod;
        vis[findd(i)]=1;
    }
    printf(&quot;%lld\n&quot;,ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>810975 (2021 CCPC Weihai M)</title><link>https://jerryblack.vercel.app/posts/810975-ccpc-weihai-en/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/810975-ccpc-weihai-en/</guid><description>Inclusion-exclusion + binomial coefficients — counting match outcomes under a longest-win-streak constraint.</description><pubDate>Sat, 05 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://codeforces.com/gym/103428/problem/M&quot;&gt;Problem link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Problem Summary&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;You played $n$ games and won exactly $m$ of them. Your longest winning streak has length exactly $k$. How many win/loss sequences match this? $(0 \le n, m, k \le 10^5)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Approach&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;At first glance this looks like a plain combinatorics problem, but it turns out to be tricky to count directly. Following &lt;a href=&quot;https://blog.csdn.net/solemntee/article/details/121686258&quot;&gt;Solemntee&apos;s editorial&lt;/a&gt;, the trick is inclusion-exclusion. Let $ans_k$ be the number of sequences whose longest winning streak is &lt;strong&gt;at least&lt;/strong&gt; $k$. The answer we want is $ans_k - ans_{k+1}$.&lt;/p&gt;
&lt;p&gt;To compute $ans_k$: first &quot;reserve&quot; the $m$ wins. Placing $i$ disjoint runs each of length at least $k$ can be encoded as choosing $i$ of the $n - m + 1$ candidate slots between/around the losses — that&apos;s $\binom{n - m + 1}{i}$. After committing $ik$ wins to those runs, the remaining $n - ik$ positions can hold the leftover wins and losses freely, contributing $\binom{n - ik}{n - m}$. By inclusion-exclusion,
$$ans_k = \sum_{i \ge 1,; ik \le m} (-1)^{i+1} \binom{n - m + 1}{i} \binom{n - ik}{n - m}.$$
Handle the corner case $k = 0$ separately (the answer is $1$ iff $m = 0$), and you&apos;re done.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
long long mod=998244353;
int MAX=100005;
vector&amp;lt;long long&amp;gt;fac,inv,finv;
void binom_init()
{
    fac.resize(MAX);
    finv.resize(MAX);
    inv.resize(MAX);
    fac[0] = fac[1] = 1;
    inv[1] = 1;
    finv[0] = finv[1] = 1;
    for (int i = 2; i &amp;lt; MAX; i++)
    {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = mod - mod / i * inv[mod % i] % mod;
        finv[i] = finv[i - 1] * inv[i] % mod;
    }
}
long long binom(long long n, long long r)
{
    if (n &amp;lt; r || n &amp;lt; 0 || r &amp;lt; 0) return 0;
    return fac[n] * finv[r] % mod * finv[n - r] % mod;
}
int main()
{
    binom_init();
    int n,m,k;
    scanf(&quot;%d%d%d&quot;,&amp;amp;n,&amp;amp;m,&amp;amp;k);
    if(k==0)
    {
        printf(&quot;%d\n&quot;,m==0);
        return 0;
    }
    long long ans1=0,ans2=0;
    for(long long i=1;i*k&amp;lt;=m;i++)
    {
        if(i&amp;amp;1)ans1=(ans1+binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
        else ans1=(ans1-binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
    }
    for(long long i=1;i*(k+1)&amp;lt;=m;i++)
    {
        if(i&amp;amp;1)ans2=(ans2+binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
        else ans2=(ans2-binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
    }
    printf(&quot;%lld\n&quot;,(ans1-ans2+mod)%mod);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>810975 (2021 CCPC 威海 M)</title><link>https://jerryblack.vercel.app/posts/810975-ccpc-weihai/</link><guid isPermaLink="true">https://jerryblack.vercel.app/posts/810975-ccpc-weihai/</guid><description>容斥 + 组合数，求给定连赢长度约束下的比赛情况数。</description><pubDate>Sat, 05 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://codeforces.com/gym/103428/problem/M&quot;&gt;传送门&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;题目大意&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;你玩了$n$把游戏，其中赢了$m$把，然后最长的连赢长度为$k$，问有多少种情况。$(0\leq n,m,k\leq10^5)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;思路&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这道题一开始想的时候还以为是一道普普通通的排列组合题，但是发现并不能很好的分析清楚。看了$Solemntee$大佬的&lt;a href=&quot;https://blog.csdn.net/solemntee/article/details/121686258&quot;&gt;题解&lt;/a&gt;后才发现原来可以用容斥做。我们可以设$ans_k$为最长连赢次数大于等于$k$的情况，然后我们所要求的答案就是$ans_k-ans_{k+1}$。然后我们来分析一下$ans_k$怎么求。我们首先可以先把赢的情况挖空，然后有$i$次连赢次数大于等于$k$的情况就是$\binom{n-m+1}{i}$，然后我们剩下的位置就可以随便放了，情况就是$\binom{n-ik}{n-m}$，于是$ans_k$的结果就是$\sum_{i=1}^{ik&amp;lt;=m}(-1)^{i+1}\binom{n-m+1}{i}\binom{n-ik}{n-m}$，然后差不多就结束了，不过还得特判一下$k==0$的情况，然后就可以$AC$啦！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
long long mod=998244353;
int MAX=100005;
vector&amp;lt;long long&amp;gt;fac,inv,finv;
void binom_init()
{
    fac.resize(MAX);
    finv.resize(MAX);
    inv.resize(MAX);
    fac[0] = fac[1] = 1;
    inv[1] = 1;
    finv[0] = finv[1] = 1;
    for (int i = 2; i &amp;lt; MAX; i++)
    {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = mod - mod / i * inv[mod % i] % mod;
        finv[i] = finv[i - 1] * inv[i] % mod;
    }
}
long long binom(long long n, long long r)
{
    if (n &amp;lt; r || n &amp;lt; 0 || r &amp;lt; 0) return 0;
    return fac[n] * finv[r] % mod * finv[n - r] % mod;
}
int main()
{
    binom_init();
    int n,m,k;
    scanf(&quot;%d%d%d&quot;,&amp;amp;n,&amp;amp;m,&amp;amp;k);
    if(k==0)
    {
        printf(&quot;%d\n&quot;,m==0);
        return 0;
    }
    long long ans1=0,ans2=0;
    for(long long i=1;i*k&amp;lt;=m;i++)
    {
        if(i&amp;amp;1)ans1=(ans1+binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
        else ans1=(ans1-binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
    }
    for(long long i=1;i*(k+1)&amp;lt;=m;i++)
    {
        if(i&amp;amp;1)ans2=(ans2+binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
        else ans2=(ans2-binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
    }
    printf(&quot;%lld\n&quot;,(ans1-ans2+mod)%mod);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>