后缀数组是从去成都之前开始学的,但是当时只是写了遍板子既不会背也不会用。从成都回来之后依然不想做这个专题,到一月中旬才填坑。刚开始因为板子不理解非常费力气,后来慢慢写着写着就会背了,背着背着也有一种理解了一点的错觉……可能并不是十分理解原理,只不过学会了怎么用而已。按自己原来的题解和日记整理一下。
入门时看的资料:五分钟并不能学会后缀数组、用法总结、罗穗骞的论文。板子是从Narcissus那学的,他的总结也十分详细,给了我很大帮助。
1.板子与套路
1 void rsort() 2 { 3 memset(tx,0,sizeof(int)*(m+1)); 4 for(int i=1;i<=n;i++) tx[rk[ty[i]]]++; 5 for(int i=1;i<=m;i++) tx[i]+=tx[i-1]; 6 for(int i=n;i>=1;i--) sa[tx[rk[ty[i]]]--]=ty[i]; 7 } 8 void suffix() 9 { 10 int i,j,k,l,p; 11 for(i=1;i<=n;i++) rk[i]=a[i],ty[i]=i; 12 m=inf,rsort(); 13 for(l=1,p=1;p<n;m=p,l<<=1) 14 { 15 for(p=0,i=n-l+1;i<=n;i++) ty[++p]=i; 16 for(i=1;i<=n;i++) if(sa[i]>l) ty[++p]=sa[i]-l; 17 rsort(),swap(rk,ty),rk[sa[1]]=p=1; 18 for(i=2;i<=n;i++) rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 19 } 20 for(i=1,k=0;i<=n;h[rk[i++]]=k) 21 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++); 22 }
$rsort$部分是一个基数排序。这是一个倍增的过程,先求出$sa$和$rk$,再根据性质快速求出$h$(即上列讲解中的$height$)。$sa[i]$的含义是排名为$i$的后缀开头的位置,$rk[i]$的含义是以$i$位置开头的后缀的排名,$h[i]$的含义是排名为$i$和$i-1$的后缀的$lcp$(最长公共前缀);$tx$和$ty$是用于实现基数排序的桶,$n$是字符串长度而$m$是字符集大小。最常见的问题是快速求两个后缀的$lcp$,答案是它们$rk$之间$h$的最小值。如果需要解决多串问题,常把多个串拼成一个并用分隔符隔断。比较裸的题有BZOJ1717和BZOJ4698。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int sj=20010; 7 int n,k,le,ri,mid,mx,pos,a[sj],rk[sj],sa[sj],m,cnt,tx[sj],ty[sj],h[sj]; 8 struct ls 9 { 10 int num,vl; 11 }s[sj]; 12 int comp(const ls&x,const ls&y) 13 { 14 return x.vl<y.vl; 15 } 16 bool check(int x) 17 { 18 for(int i=1;i<=n;i=pos+1) 19 { 20 pos=i; 21 while(h[pos]>=x) pos++; 22 if(pos-i>=k-1) return 1; 23 if(pos!=i) pos--; 24 } 25 return 0; 26 } 27 void rsort() 28 { 29 memset(tx,0,sizeof(int)*(m+1)); 30 for(int i=1;i<=n;++i) tx[rk[ty[i]]]++; 31 for(int i=1;i<=m;++i) tx[i]+=tx[i-1]; 32 for(int i=n;i>=1;--i) sa[tx[rk[ty[i]]]--]=ty[i]; 33 } 34 void suffix() 35 { 36 int i,j,k,p,l; 37 for(int i=1;i<=n;++i) rk[i]=a[i],ty[i]=i; 38 m=sj-10;rsort(); 39 for(p=1,l=1;p<n;m=p,l<<=1) 40 { 41 for(p=0,i=n-l+1;i<=n;++i) ty[++p]=i; 42 for(i=1;i<=n;++i) if(sa[i]>l) ty[++p]=sa[i]-l; 43 rsort(),swap(rk,ty),rk[sa[1]]=p=1; 44 for(i=2;i<=n;++i) rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 45 } 46 for(i=1,k=0;i<=n;h[rk[i++]]=k) 47 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k); 48 } 49 int main() 50 { 51 scanf("%d%d",&n,&k); 52 for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i].vl=a[i],s[i].num=i; 53 sort(s+1,s+n+1,comp); 54 a[s[1].num]=1,mx=1; 55 for(int i=2;i<=n;i++) 56 { 57 if(s[i-1].vl!=s[i].vl) mx++; 58 a[s[i].num]=mx; 59 } 60 le=1,ri=n; 61 suffix(); 62 while(le<ri) 63 { 64 mid=(le+ri+1)>>1; 65 if(check(mid)) le=mid; 66 else ri=mid-1; 67 } 68 printf("%d",le); 69 return 0; 70 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int sj=1000010; 6 const int bd=2000; 7 int n,m,h[sj],tx[sj],ty[sj],rk[sj],sa[sj],a[sj]; 8 int le,ri,mid,num,bl[sj],pos,la,nt,cnt; 9 bool r[sj]; 10 void rsort() 11 { 12 memset(tx,0,sizeof(int)*(m+1)); 13 for(int i=1;i<=cnt;++i) tx[rk[ty[i]]]++; 14 for(int i=1;i<=m;++i) tx[i]+=tx[i-1]; 15 for(int i=cnt;i>=1;--i) sa[tx[rk[ty[i]]]--]=ty[i]; 16 } 17 void getsa() 18 { 19 int i,j,p,l,k; 20 for(i=1;i<=cnt;++i) rk[i]=a[i],ty[i]=i; 21 m=4000,rsort(); 22 for(p=1,l=1;p<cnt;m=p,l<<=1) 23 { 24 for(p=0,i=cnt-l+1;i<=cnt;++i) ty[++p]=i; 25 for(i=0;i<=cnt;++i) if(sa[i]>l) ty[++p]=sa[i]-l; 26 rsort(),swap(rk,ty),rk[sa[1]]=p=1; 27 for(i=2;i<=cnt;++i) 28 rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 29 } 30 for(i=1,k=0;i<=cnt;h[rk[i++]]=k) 31 for(k=k?k-1:0,j=sa[rk[i]-1];a[i+k]==a[j+k];++k); 32 } 33 bool check(int x) 34 { 35 for(int i=1;i<=cnt;i=pos+1) 36 { 37 pos=i,num=0; 38 while(h[pos]>=x) 39 { 40 if(bl[sa[pos]]&&!r[bl[sa[pos]]]) num++; 41 r[bl[sa[pos]]]=1; 42 pos++; 43 } 44 if(h[i]>=x) 45 if(bl[sa[i-1]]&&!r[bl[sa[i-1]]]) num++; 46 for(int j=i;j<pos;++j) r[bl[sa[j]]]=0; 47 if(num==n) return 1; 48 } 49 return 0; 50 } 51 int main() 52 { 53 scanf("%d",&n); 54 ri=0x7fffffff; 55 for(int i=1;i<=n;++i) 56 { 57 scanf("%d",&m); 58 if(m<ri) ri=m; 59 for(int j=1;j<=m;++j) 60 { 61 scanf("%d",&nt); 62 a[++cnt]=nt-la+bd,la=nt; 63 bl[cnt]=i; 64 } 65 ++cnt,la=0; 66 } 67 ri--; 68 getsa(); 69 while(le<ri) 70 { 71 mid=(le+ri+1)>>1; 72 if(check(mid)) le=mid; 73 else ri=mid-1; 74 } 75 if(n==1) printf("0"); 76 else printf("%d",le+1); 77 return 0; 78 }
2.BZOJ3238 差异
观察题目中给的式子,每个后缀的长度都会被加$n-1$次,后缀的长度又是一个公差为$1$的等差数列,可以直接用求和公式$n*(n+1)/2*(n-1)$完成统计。剩下的就是后缀之间两两$lcp$的长度和,可以转化为每个长度*$lcp$等于该长度的后缀数。$lcp$是$rk$区间$h$的最小值,可以用单调栈求出每个$h$控制的范围,就变成暑假集训常做的那种NOIP模拟题了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<stack> 5 #define ll long long 6 using namespace std; 7 const int sj=500010; 8 stack<int> q; 9 int tx[sj],ty[sj],rk[sj],sa[sj],m,h[sj],a[sj],lm[sj],rm[sj]; 10 char s[sj]; 11 ll ans,n; 12 void rsort() 13 { 14 memset(tx,0,sizeof(int)*(m+1)); 15 for(int i=1;i<=n;i++) tx[rk[ty[i]]]++; 16 for(int i=1;i<=m;i++) tx[i]+=tx[i-1]; 17 for(int i=n;i>=1;i--) sa[tx[rk[ty[i]]]--]=ty[i]; 18 } 19 void suffix() 20 { 21 int i,j,k,l,p; 22 for(i=1;i<=n;i++) rk[i]=a[i],ty[i]=i; 23 m=26;rsort(); 24 for(l=1,p=1;p<n;m=p,l<<=1) 25 { 26 for(p=0,i=n-l+1;i<=n;i++) ty[++p]=i; 27 for(i=1;i<=n;i++) if(sa[i]>l) ty[++p]=sa[i]-l; 28 rsort(),swap(ty,rk),rk[sa[1]]=p=1; 29 for(i=2;i<=n;i++) 30 rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 31 } 32 for(i=1,k=0;i<=n;h[rk[i++]]=k) 33 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++); 34 } 35 int main() 36 { 37 scanf("%s",s); 38 n=strlen(s); 39 ans=n*(n+1)/2*(n-1); 40 for(int i=1;i<=n;i++) a[i]=s[i-1]-‘a‘+1; 41 suffix(); 42 q.push(0),h[0]=-1; 43 for(int i=1;i<=n;i++) 44 { 45 while(h[q.top()]>=h[i]) q.pop(); 46 lm[i]=q.top()+1; 47 q.push(i); 48 } 49 while(!q.empty()) q.pop(); 50 q.push(n+1),h[n+1]=-1; 51 for(int i=n;i>=1;i--) 52 { 53 while(h[q.top()]>h[i]) q.pop(); 54 rm[i]=q.top()-1; 55 q.push(i); 56 } 57 for(ll i=1;i<=n;i++) 58 ans-=(i-lm[i]+1)*(rm[i]-i+1)*2*h[i]; 59 printf("%lld",ans); 60 return 0; 61 }
3.BZOJ4556 字符串
求一个字符串内一个子串和另一个子串的子串们的$lcp$。$lcp$的长度受到h和两个子串长度的双重限制,于是考虑二分答案来确定可行区间的端点。例如我们要求$[a,b]$的子串和$[c,d]$的$lcp$,二分$lcp$长度为$mid$,那么只能是开头在$[a,b-mid+1]$的后缀和$[c,d]$的$lcp$。维护一个下标为字符串中对应位置、权值为$rk$的线段树,每次$check$利用$h$数组二分两次求出$rk$在$[l,r]$的区间满足要求,再在主席树上查询$[a,b-mid+1]$中是否有rk落在$[l,r]$中。总复杂度$mlog^2n$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int sj=100010; 6 int rk[sj],tx[sj],ty[sj],sa[sj],h[sj],a[sj],p[sj][20]; 7 int rt[sj],n,q,a1,a2,a3,a4,l,r,mid,lm,rm,cnt,m; 8 char s[sj]; 9 struct tree 10 { 11 int sum,lc,rc; 12 }t[sj*50]; 13 inline int read() 14 { 15 int jg=0,jk=getchar()-‘0‘; 16 while(jk<0||jk>9) jk=getchar()-‘0‘; 17 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-‘0‘; 18 return jg; 19 } 20 inline int bj(int x,int y) 21 { 22 return x<y?x:y; 23 } 24 void pre() 25 { 26 for(int i=1;i<=n;i++) p[i][0]=h[i]; 27 for(int j=1;(1<<j)<=n;j++) 28 for(int i=1;i+(1<<j)-1<=n;i++) 29 p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]); 30 } 31 void rsort() 32 { 33 memset(tx,0,sizeof(int)*(m+1)); 34 for(int i=1;i<=n;i++) tx[rk[ty[i]]]++; 35 for(int i=1;i<=m;i++) tx[i]+=tx[i-1]; 36 for(int i=n;i>=1;i--) sa[tx[rk[ty[i]]]--]=ty[i]; 37 } 38 void suffix() 39 { 40 int i,j,k,p,l; 41 for(int i=1;i<=n;i++) rk[i]=a[i],ty[i]=i; 42 m=26;rsort(); 43 for(p=1,l=1;p<n;m=p,l<<=1) 44 { 45 for(p=0,i=n-l+1;i<=n;i++) ty[++p]=i; 46 for(i=1;i<=n;i++) if(sa[i]>l) ty[++p]=sa[i]-l; 47 rsort(),swap(rk,ty),rk[sa[1]]=p=1; 48 for(i=2;i<=n;i++) rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 49 } 50 for(i=1,k=0;i<=n;h[rk[i++]]=k) 51 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k); 52 } 53 void insert(int nt,int la,int z,int y,int vl) 54 { 55 t[nt].sum=t[la].sum+1,t[nt].lc=t[la].lc,t[nt].rc=t[la].rc; 56 if(z==y) return; 57 int mi=z+y>>1; 58 if(vl<=mi) t[nt].lc=++cnt,insert(t[nt].lc,t[la].lc,z,mi,vl); 59 else t[nt].rc=++cnt,insert(t[nt].rc,t[la].rc,mi+1,y,vl); 60 } 61 bool query(int nt,int la,int z,int y,int le,int ri) 62 { 63 if(t[la].sum-t[nt].sum==0) return 0; 64 if(z==le&&y==ri) return 1; 65 int mi=z+y>>1; 66 if(ri<=mi) return query(t[nt].lc,t[la].lc,z,mi,le,ri); 67 if(le>mi) return query(t[nt].rc,t[la].rc,mi+1,y,le,ri); 68 bool p1=query(t[nt].lc,t[la].lc,z,mi,le,mi); 69 bool p2=query(t[nt].rc,t[la].rc,mi+1,y,mi+1,ri); 70 return (p1|p2); 71 } 72 int main() 73 { 74 n=read(),q=read(); 75 scanf("%s",s); 76 for(int i=1;i<=n;i++) a[i]=s[i-1]-‘a‘+1; 77 suffix();pre(); 78 for(int i=1;i<=n;i++) 79 { 80 rt[i]=++cnt; 81 insert(rt[i],rt[i-1],1,n,rk[i]); 82 } 83 for(int i=1;i<=q;i++) 84 { 85 a1=read(),a2=read(),a3=read(),a4=read(); 86 l=0,r=bj(a4-a3+1,a2-a1+1); 87 while(l<r) 88 { 89 mid=(l+r+1)>>1,lm=rm=rk[a3]; 90 for(int j=16;j>=0;j--) 91 if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=mid) 92 lm-=(1<<j); 93 for(int j=16;j>=0;j--) 94 if(rm+(1<<j)<=n&&p[rm+1][j]>=mid) 95 rm+=(1<<j); 96 if(query(rt[a1-1],rt[a2-mid+1],1,n,lm,rm)) l=mid; 97 else r=mid-1; 98 } 99 printf("%d\n",l); 100 } 101 return 0; 102 }
4.BZOJ2754 喵星球上的点名
暴力水过去的,并不知道正解应该怎么写。把所有姓名和询问都接成一个串,对每一个询问RMQ求出可行区间,然后就暴力扫可行区间统计答案。实际上复杂度非常没保障。 COGS上数据是学长强化过的,貌似只有cooook过了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 inline int read() 6 { 7 int jg=0,jk=getchar()-‘0‘; 8 while(jk<0||jk>9) jk=getchar()-‘0‘; 9 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-‘0‘; 10 return jg; 11 } 12 const int sj=300010; 13 const int inf=10001; 14 int h[sj],rk[sj],sa[sj],tx[sj],ty[sj],cnt,q,m,a[sj],n,len,a1,bl[sj],p[sj][20]; 15 int st[50010],ln[50010],num[20010],ans,lm,rm; 16 bool r[20010]; 17 inline int bj(int x,int y) 18 { 19 return x<y?x:y; 20 } 21 void pre() 22 { 23 for(int i=1;i<=n;i++) p[i][0]=h[i]; 24 for(int j=1;(1<<j)<=n;j++) 25 for(int i=1;i+(1<<j)-1<=n;i++) 26 p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]); 27 } 28 void rsort() 29 { 30 memset(tx,0,sizeof(int)*(m+1)); 31 for(int i=1;i<=n;i++) tx[rk[ty[i]]]++; 32 for(int i=1;i<=m;i++) tx[i]+=tx[i-1]; 33 for(int i=n;i>=1;i--) sa[tx[rk[ty[i]]]--]=ty[i]; 34 } 35 void suffix() 36 { 37 int i,j,k,l,p; 38 for(i=1;i<=n;i++) rk[i]=a[i],ty[i]=i; 39 m=inf,rsort(); 40 for(l=1,p=1;p<n;m=p,l<<=1) 41 { 42 for(p=0,i=n-l+1;i<=n;i++) ty[++p]=i; 43 for(i=1;i<=n;i++) if(sa[i]>l) ty[++p]=sa[i]-l; 44 rsort(),swap(rk,ty),rk[sa[1]]=p=1; 45 for(i=2;i<=n;i++) rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 46 } 47 for(i=1,k=0;i<=n;h[rk[i++]]=k) 48 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++); 49 } 50 int main() 51 { 52 cnt=read(),q=read(); 53 for(int i=1;i<=cnt;i++) 54 { 55 len=read(); 56 for(int j=1;j<=len;j++) 57 a1=read(),a[++n]=a1,bl[n]=i; 58 a[++n]=inf; 59 len=read(); 60 for(int j=1;j<=len;j++) 61 a1=read(),a[++n]=a1,bl[n]=i; 62 a[++n]=inf; 63 } 64 for(int i=1;i<=q;i++) 65 { 66 ln[i]=read(),st[i]=n+1; 67 for(int j=1;j<=ln[i];j++) 68 a1=read(),a[++n]=a1; 69 a[++n]=inf; 70 } 71 suffix();pre(); 72 for(int i=1;i<=q;i++) 73 { 74 lm=rm=rk[st[i]],ans=0; 75 for(int j=19;j>=0;j--) 76 if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=ln[i]) 77 lm-=(1<<j); 78 for(int j=19;j>=0;j--) 79 if(rm+(1<<j)<=n&&p[rm+1][j]>=ln[i]) 80 rm+=(1<<j); 81 for(int j=lm;j<=rm;j++) 82 if(bl[sa[j]]&&!r[bl[sa[j]]]) 83 r[bl[sa[j]]]=1,num[bl[sa[j]]]++,ans++; 84 for(int j=lm;j<=rm;j++) 85 if(bl[sa[j]]) 86 r[bl[sa[j]]]=0; 87 printf("%d\n",ans); 88 } 89 for(int i=1;i<cnt;i++) 90 printf("%d ",num[i]); 91 printf("%d",num[cnt]); 92 return 0; 93 }
5.BZOJ3230 相似子串
我居然没有在BZOJ上交这题?回校再写吧,当初好像还在代码后面附了题解的。
6.BZOJ4199 品酒大会
充分利用height数组的性质。在n相似的时候没有任何两杯酒满足要求,而0相似的时候任意两杯酒都满足要求;满足要求的总是$rk$在一个区间里的所有酒,两问的答案都可以通过区间计算得到。如果我们从大到小处理,相当于一个区间合并、更新答案的过程。注意有负权存在。刚开始犯蠢写了个主席树,T了之后才发现这完全是并查集就能解决的问题……
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 using namespace std; 7 const int sj=300010; 8 int n,m,a[sj],tx[sj],ty[sj],rk[sj],sa[sj],h[sj],a1,a2,fa[sj]; 9 ll vl[sj],ans1[sj],ans2[sj],sum,ans,tp1,tp2,tp,tp3,tp4,qwq,mx[sj],mi[sj],num[sj]; 10 char s[sj]; 11 struct ls 12 { 13 int num,val; 14 }ss[sj]; 15 struct tree 16 { 17 int lc,rc,sum; 18 }t[sj*30]; 19 int comp(const ls&x,const ls&y) 20 { 21 return x.val<y.val; 22 } 23 void rsort() 24 { 25 memset(tx,0,sizeof(int)*(m+1)); 26 for(int i=1;i<=n;i++) tx[rk[ty[i]]]++; 27 for(int i=1;i<=m;i++) tx[i]+=tx[i-1]; 28 for(int i=n;i>=1;i--) sa[tx[rk[ty[i]]]--]=ty[i]; 29 } 30 void suffix() 31 { 32 int i,j,k,l,p; 33 for(i=1;i<=n;i++) rk[i]=a[i],ty[i]=i; 34 m=26;rsort(); 35 for(p=1,l=1;p<n;m=p,l<<=1) 36 { 37 for(p=0,i=n-l+1;i<=n;i++) ty[++p]=i; 38 for(i=1;i<=n;i++) if(sa[i]>l) ty[++p]=sa[i]-l; 39 rsort(),swap(ty,rk),rk[sa[1]]=p=1; 40 for(i=2;i<=n;i++) 41 rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p; 42 } 43 for(i=1,k=0;i<=n;h[rk[i++]]=k) 44 for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++); 45 } 46 void init() 47 { 48 scanf("%d%s",&n,s); 49 memset(fa,-1,sizeof(fa)); 50 tp1=-1ll<<62,tp2=-1ll<<62,tp3=1ll<<62,tp4=1ll<<62; 51 for(int i=1;i<=n;i++) 52 { 53 scanf("%lld",&vl[i]); 54 a[i]=s[i-1]-‘a‘+1; 55 if(vl[i]>=tp1) tp2=tp1,tp1=vl[i]; 56 else if(vl[i]>tp2) tp2=vl[i]; 57 if(vl[i]<=tp3) tp4=tp3,tp3=vl[i]; 58 else if(vl[i]<tp4) tp4=vl[i]; 59 } 60 } 61 int find(int x) 62 { 63 if(fa[x]==-1) return x; 64 return fa[x]=find(fa[x]); 65 } 66 inline ll maxx(ll x,ll y) 67 { 68 return x>y?x:y; 69 } 70 inline ll minn(ll x,ll y) 71 { 72 return x<y?x:y; 73 } 74 int main() 75 { 76 init(); 77 suffix(); 78 for(int i=1;i<=n;i++) 79 { 80 ss[i].num=i,ss[i].val=h[i]; 81 num[i]=1,mx[i]=mi[i]=vl[sa[i]]; 82 } 83 sort(ss+1,ss+n+1,comp); 84 ans=-1ll<<62; 85 for(int i=n;i>=1;i--) 86 { 87 if(ss[i].val!=ss[i+1].val) 88 ans1[ss[i+1].val]=sum/2,ans2[ss[i+1].val]=ans; 89 a1=ss[i].num,a2=a1-1; 90 if(!h[a1]) break; 91 a1=find(a1),a2=find(a2); 92 sum-=(ll)num[a1]*(num[a1]-1); 93 sum-=(ll)num[a2]*(num[a2]-1); 94 tp=mx[a1]*mx[a2],qwq=mi[a1]*mi[a2]; 95 if(tp>ans) ans=tp; 96 if(qwq>ans) ans=qwq; 97 if(num[a1]<num[a2]) swap(a1,a2); 98 mx[a1]=maxx(mx[a1],mx[a2]); 99 mi[a1]=minn(mi[a1],mi[a2]); 100 num[a1]+=num[a2]; 101 fa[a2]=a1; 102 sum+=(ll)num[a1]*(num[a1]-1); 103 } 104 ans1[0]=(ll)n*(n-1)/2,ans2[0]=maxx(tp1*tp2,tp3*tp4); 105 for(int i=0;i<n;i++) 106 printf("%lld %lld\n",ans1[i],ans2[i]); 107 return 0; 108 }