Codeforces https://codeforces.com/gym/100962/problem/D
思路
感觉这个离线的思路好神仙啊 qwq
对于每个询问 \([l,r]\) 其实就是要求 \(p_{max}\), 使得 \(lcs(s[1,p],s[1,r])>p-l\), 也就是 \(lcs(s[1,p],s[1,r])+l>p\).
首先把询问离线按 \(r\) 排序, 然后从右往左扫, 每次
处理之前已经被加进去的询问, 看当前位置是否能被作为 \(p\), 然后把已经处理完毕的询问给删掉.
把当前询问塞进去.
建出反串的后缀树, 那么不等式左边就是 \(dep_{lca(p,r)}+l\).
暴力的做法就是枚举 \(lca\), 然后处理询问的时候查一下这个位置的 \(l\), 塞询问的时候把 \(l\) 塞进去.
用树链剖分加速: 对于每一条链把 \(tag\) 打在深度最深的地方. 处理询问的时候就查一下 \(p\) 到根上的 \(\max(dep_x+l)\) 以及这个 \(tag\) 来自哪个询问, 然后将它删掉. 塞询问的时候就一路往上跳, 打 \(tag\).
怎么查询? 分最大的 \(tag\) 是在上面还是下面来讨论, 如果是上面就直接查, 否则查一下 \(\max(l)\) 再加上当前的 \(dep\).
由于每个点只能有一个最大的 \(tag\) 放在线段树上, 所以还要用一个 \(set\) 记录这个点有哪些标记.
(可能讲得不是很清楚 qwq)
代码
- #include<bits/stdc++.h>
- clock_t t=clock();
- namespace my_std{
- using namespace std;
- #define pii pair<int,int>
- #define fir first
- #define sec second
- #define MP make_pair
- #define rep(i,x,y) for (int i=(x);i<=(y);i++)
- #define drep(i,x,y) for (int i=(x);i>=(y);i--)
- #define go(x) for (int i=head[x];i;i=edge[i].nxt)
- #define templ template<typename T>
- #define sz 402020
- typedef long long ll;
- typedef double db;
- mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
- templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
- templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
- templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
- templ inline void read(T& t)
- {
- t=0;char f=0,ch=getchar();double d=0.1;
- while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
- while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
- if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
- t=(f?-t:t);
- }
- template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
- char __sr[1<<21],__z[20];int __C=-1,__zz=0;
- inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
- inline void print(register int x)
- {
- if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
- while(__z[++__zz]=x%10+48,x/=10);
- while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
- }
- void file()
- {
- #ifdef NTFOrz
- freopen("a.in","r",stdin);
- #endif
- }
- inline void chktime()
- {
- #ifndef ONLINE_JUDGE
- cout<<(clock()-t)/1000.0<<'\n';
- #endif
- }
- #ifdef mod
- ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
- ll inv(ll x){return ksm(x,mod-2);}
- #else
- ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
- #endif
- // inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
- }
- using namespace my_std;
- int n,Q;
- char s[sz];
- int pos[sz];
- struct hh{int l,r,id;const bool operator <(const hh &x) const {return r>x.r;}}q[sz];
- int ans[sz];
- int TT;
- //struct SegmentTree
- //{
- pii tr[sz<<2][2];
- #define ls k<<1
- #define rs k<<1|1
- #define lson ls,l,mid
- #define rson rs,mid+1,r
- void pushup(int k){tr[k][TT]=max(tr[ls][TT],tr[rs][TT]);}
- void modify(int k,int l,int r,int x,pii w)
- {
- if (l==r) return (void)(tr[k][TT]=w);
- int mid=(l+r)>>1;
- if (x<=mid) modify(lson,x,w);
- else modify(rson,x,w);
- pushup(k);
- }
- pii query(int k,int l,int r,int x,int y)
- {
- if (x<=l&&r<=y) return tr[k][TT];
- int mid=(l+r)>>1;pii ret=MP(-1e9,0);
- if (x<=mid) chkmax(ret,query(lson,x,y));
- if (y>mid) chkmax(ret,query(rson,x,y));
- return ret;
- }
- //}T1,T2;
- // 1: 维护一段里面最大的值是多少, 以及它的位置.
- // 2: 维护一段里面加的 l 的最大值是多少, 以及它的位置.
- namespace Tree
- {
- struct hh{int t,nxt;}edge[sz];
- int head[sz],ecnt;
- void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
- int dfn[sz],top[sz],bot[sz],size[sz],son[sz],fa[sz],len[sz],T;
- #define v edge[i].t
- void dfs1(int x)
- {
- size[x]=1;
- go(x)
- {
- fa[v]=x;dfs1(v);
- size[x]+=size[v];
- if (size[v]>size[son[x]]) son[x]=v;
- }
- }
- void dfs2(int x,int tp)
- {
- dfn[x]=++T;
- top[x]=tp,bot[tp]=x;
- if (son[x]) dfs2(son[x],tp);
- go(x) if (v!=son[x]) dfs2(v,v);
- }
- #undef v
- set<pii>s[sz];
- // 维护这个地方打的标记有哪些
- pii query(int x)
- {
- pii ret=MP(0,0);
- while (x)
- {
- TT=0;pii w1=::query(1,1,T,dfn[top[x]],dfn[x]);
- TT=1;pii w2=::query(1,1,T,dfn[x],dfn[bot[top[x]]]);
- w2.fir+=len[x];
- chkmax(ret,w1),chkmax(ret,w2);
- x=fa[top[x]];
- }
- return ret;
- }
- void modify(int x,int w,int id,bool add)
- {
- while (x)
- {
- if (add) s[x].insert(MP(w,id));
- else s[x].erase(MP(w,id));
- if (s[x].size())
- {
- auto cur=*s[x].rbegin();
- TT=1;::modify(1,1,T,dfn[x],cur);
- cur.fir+=len[x];
- TT=0;::modify(1,1,T,dfn[x],cur);
- }
- else TT=0,::modify(1,1,T,dfn[x],MP(0,0)),TT=1,::modify(1,1,T,dfn[x],MP(0,0));
- x=fa[top[x]];
- }
- }
- }
- namespace SAM
- {
- struct hh{int link,len,ch[28];}a[sz];
- int lst=1,cnt=1,root=1;
- int add(int c)
- {
- int cur=++cnt,p=lst;lst=cur;a[cur].len=a[p].len+1;
- while (p&&!a[p].ch[c]) a[p].ch[c]=cur,p=a[p].link;
- if (!p) return a[cur].link=1,cur;
- int q=a[p].ch[c];
- if (a[q].len==a[p].len+1) return a[cur].link=q,cur;
- int t=++cnt;a[t]=a[q];a[q].link=a[cur].link=t;a[t].len=a[p].len+1;
- while (p&&a[p].ch[c]==q) a[p].ch[c]=t,p=a[p].link;
- return cur;
- }
- void build(){rep(i,2,cnt) Tree::make_edge(a[i].link,i),Tree::len[i]=a[i].len;}
- }
- int main()
- {
- file();
- read(n,Q);
- cin>>(s+1);
- rep(i,1,n) pos[i]=SAM::add(s[i]-'a'+1);
- SAM::build();
- Tree::dfs1(1),Tree::dfs2(1,1);
- rep(i,1,Q) read(q[i].l,q[i].r),q[i].id=i;
- sort(q+1,q+Q+1);
- int p=1;
- drep(i,n,1)
- {
- while (233)
- {
- pii w=Tree::query(pos[i]);int id=w.sec;
- if (w.fir<=i) break;
- ans[q[id].id]=i-q[id].l+1;
- Tree::modify(pos[q[id].r],q[id].l,id,0);
- }
- while (p<=Q&&q[p].r==i) Tree::modify(pos[q[p].r],q[p].l,p,1),++p;
- }
- rep(i,1,Q) printf("%d\n",ans[i]);
- return 0;
- }
来源: http://www.bubuko.com/infodetail-3147769.html