[雅礼集训 2017 Day7] 事情的相似度
一、题目
二、题目
后缀自动机乱杀。
他问的是前缀之间的最长后缀,我们对正串建出后缀自动机,然后把前缀在自动机上面打上标记。根据后缀自动机的性质,最长后缀就是两个前缀在 \(\tt parent \;tree\) 上 \(\tt lca\) 的 \(len\)
对于一个前缀对 \((l,r)\) ,那么他可以对 \(L\leq l,r\leq R\) 的询问 \((L,R)\) 产生贡献。因为是 \(\tt lca\) ,所以可以想到通过树上启发式合并找出这些前缀对,那么我们在合并子树的时候就要考虑子树之间新产生的点对。
貌似这样前缀对的数量是 \(O(n^2)\) 的,合并的时候我们只需要找最接近的前缀,因为贡献都是一样的,但是找最接近的能贡献到的是最多的,所以数量是 \(O(n\log n)\),用 \(\tt set\) 维护启发式合并。
那么时间复杂度 \(O(n\log^2 n)\),最后算贡献的时候本质上是一个二维偏序,所以直接排序就行了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 200005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,tot,cnt,last,bit[M],f[M],ans[M];char t[M];
set<int> s[M];
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
struct node
{
int fa,ch[2],len;
}a[M];
struct zxy
{
int l,r,x;
bool operator < (const zxy &b) const
{
return r<b.r;
}
}q[M],p[20*M];
void add(int c)
{
int p=last,np=last=++cnt;
a[np].len=a[p].len+1;
for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
if(!p) a[np].fa=1;
else
{
int q=a[p].ch[c];
if(a[q].len==a[p].len+1) a[np].fa=q;
else
{
int nq=++cnt;
a[nq]=a[q];a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;
for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
}
}
}
void dfs(int u,int fa)
{
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
if(s[u].size()<s[v].size()) swap(s[u],s[v]);
set<int>::iterator it,pre,nxt;
for(it=s[v].begin();it!=s[v].end();it++)
{
s[u].insert(*it);
pre=nxt=s[u].find(*it);nxt++;
if(pre!=s[u].begin()) pre--,p[++k]=zxy{*pre,*it,a[u].len};
if(nxt!=s[u].end()) p[++k]=zxy{*it,*nxt,a[u].len};
s[u].erase(*it);
}
for(it=s[v].begin();it!=s[v].end();it++)
s[u].insert(*it);
}
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int y)
{
for(int i=x;i>=1;i-=lowbit(i))
bit[i]=max(bit[i],y);
}
int ask(int x)
{
int res=0;
for(int i=x;i<=n;i+=lowbit(i))
res=max(res,bit[i]);
return res;
}
signed main()
{
n=read();m=read();
scanf("%s",t+1);
cnt=last=1;
for(int i=1;i<=n;i++)
{
add(t[i]-'0');
s[last].insert(i);
}
for(int i=2;i<=cnt;i++)
{
int j=a[i].fa;
e[++tot]=edge(i,f[j]),f[j]=tot;
e[++tot]=edge(j,f[i]),f[i]=tot;
}
dfs(1,0);
sort(p+1,p+1+k);
for(int i=1;i<=m;i++)
{
int l=read(),r=read();
q[i]=zxy{l,r,i};
}
sort(q+1,q+1+m);
for(int i=1,j=1;i<=m;i++)
{
while(j<=k && p[j].r<=q[i].r) add(p[j].l,p[j].x),j++;
ans[q[i].x]=ask(q[i].l);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}