【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
Description
人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。
你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。
你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?
Input
第一行两个整数 n、m,表示串长和询问个数。
第二行长度为 n 的 01 串,表示历史的行程。
接下来 m 行,每行两个正整数 l 、r 表示询问的区间,包括端点,保证1≤l<r≤n。
Output
输出 m 行,对每个询问输出一个整数表示最大的相似度。
建出原串的SAM,则两个前缀的最长公共后缀为他们在parent树上的lca,问题转化为求区间内前缀两两lca深度的最大值。
将询问离线,按右端点从小到大排序。我们考虑每次加入一个字母,就将他们在parent树上到根节点的路径打上他们的标记。往根节点跑的过程中,若遇到了以前打的标记,则该节点为旧标记与新标记的lca。贪心可得应把标记尽量覆盖为较大的值。用树状数组来统计答案,下标为左端点,每次查询下标大于等于该询问左端点的最大深度。向根跑的过程中每一次遇到旧标记,就在树状数组上更新答案,并给该节点打上新标记。
往根节点跑的过程实际上就是LCT的access。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #define LL long long 6 using namespace std; 7 const int N=1e5+5; 8 int n,m,r,last,size,root; 9 int p[N],mx[N],ans[N],num[N]; 10 int c[N*2][2],fa[N*2],v[N*2],tag[N*2]; 11 char s[N]; 12 vector<int> q[N]; 13 struct sam{int mx,fa,ch[2];}t[N*2]; 14 int read() 15 { 16 int x=0,f=1;char c=getchar(); 17 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 18 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 19 return x*f; 20 } 21 int lowbit(int x){return x&(-x);} 22 void modify(int x,int v){x=n-x+1;while(x<=n)mx[x]=max(mx[x],v),x+=lowbit(x);} 23 int query(int x){x=n-x+1;int ans=0;while(x)ans=max(ans,mx[x]),x-=lowbit(x);return ans;} 24 void ins(int c,int id) 25 { 26 int np=++size;num[id]=np; 27 t[np].mx=t[last].mx+1; 28 int x=last;last=np; 29 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 30 if(!x)t[np].fa=root; 31 else 32 { 33 int y=t[x].ch[c]; 34 if(t[y].mx==t[x].mx+1)t[np].fa=y; 35 else 36 { 37 int nq=++size; 38 t[nq]=t[y];t[nq].mx=t[x].mx+1; 39 t[y].fa=t[np].fa=nq; 40 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 41 } 42 } 43 } 44 bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;} 45 void change(int x,int val){v[x]=tag[x]=val;} 46 void down(int x) 47 { 48 if(!tag[x])return; 49 if(c[x][0])change(c[x][0],tag[x]); 50 if(c[x][1])change(c[x][1],tag[x]); 51 tag[x]=0; 52 } 53 void rotate(int x) 54 { 55 int y=fa[x],z=fa[y],l,r; 56 if(c[y][0]==x)l=0;else l=1;r=l^1; 57 if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;} 58 fa[x]=z;fa[y]=x;fa[c[x][r]]=y; 59 c[y][l]=c[x][r];c[x][r]=y; 60 } 61 void relax(int x){if(!isroot(x))relax(fa[x]);down(x);} 62 void splay(int x) 63 { 64 relax(x); 65 while(!isroot(x)) 66 { 67 int y=fa[x],z=fa[y]; 68 if(!isroot(y)) 69 { 70 if((c[y][0]==x)^(c[z][0]==y))rotate(x); 71 else rotate(y); 72 } 73 rotate(x); 74 } 75 } 76 void access(int x,int val) 77 { 78 int o=0; 79 while(x) 80 { 81 splay(x);modify(v[x],t[x].mx); 82 c[x][1]=o;o=x;x=fa[x]; 83 } 84 tag[o]=v[o]=val; 85 } 86 void build(){for(int i=1;i<=size;i++)fa[i]=t[i].fa;} 87 int main() 88 { 89 n=read();m=read(); 90 scanf("%s",s+1); 91 for(int i=1;i<=m;i++) 92 { 93 p[i]=read();r=read(); 94 q[r].push_back(i); 95 } 96 last=size=root=1; 97 for(int i=1;i<=n;i++)ins(s[i]-'0',i); 98 build(); 99 for(int i=1;i<=n;i++) 100 { 101 access(num[i],i); 102 int sz=q[i].size(); 103 for(int j=0;j<sz;j++) 104 { 105 int x=q[i][j]; 106 ans[x]=query(p[x]); 107 } 108 } 109 for(int i=1;i<=m;i++)printf("%d\n",ans[i]); 110 return 0; 111 }