这道题可以转化为计数类问题。
若使用后缀数组,那么答案就是所有位置二元组(i,j)的lcp对0~lcp答案段的贡献。然后发现若一个二元组有x的贡献,那么对x-1有同样的贡献,考虑先求出lcp(max)的答案,再传给lcp(max-1)等等,复杂度是O(N)的。
若用SAM,那么需要求的答案在x与fa[x]的转移之间,因为后缀自动机中每个点所代表的出现位置和出现次数是相等的,可以合并,事实上还是用到了后缀数组的叠加的原理,从叶子节点一路传上去……………………………………………
还有要注意INF要足够大。
1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 const int maxn=300010; 7 const long long INF=3000000000000000000LL; 8 int rank[maxn],sa[maxn],r[maxn],lcp[maxn]; 9 int Wa[maxn],Wb[maxn],Wv[maxn],Ws[maxn]; 10 11 bool cmp(int *p,int i,int j,int l){ 12 return p[i]==p[j]&&p[i+l]==p[j+l]; 13 } 14 15 void DA(int n,int m){ 16 int i,h,p=1,*x=Wa,*y=Wb; 17 for(i=0;i<m;i++)Ws[i]=0; 18 for(i=0;i<n;i++)Ws[x[i]=r[i]]++; 19 for(i=1;i<m;i++)Ws[i]+=Ws[i-1]; 20 for(i=n-1;i>=0;i--)sa[--Ws[x[i]]]=i; 21 22 23 for(h=1;p<n;m=p,h<<=1){ 24 for(i=n-h,p=0;i<n;i++)y[p++]=i; 25 for(i=0;i<n;i++) 26 if(sa[i]>=h)y[p++]=sa[i]-h; 27 for(i=0;i<m;i++)Ws[i]=0; 28 for(i=0;i<n;i++)Ws[Wv[i]=x[y[i]]]++; 29 for(i=1;i<m;i++)Ws[i]+=Ws[i-1]; 30 for(i=n-1;i>=0;i--)sa[--Ws[Wv[i]]]=y[i]; 31 for(p=1,swap(x,y),x[sa[0]]=0,i=1;i<n;i++) 32 x[sa[i]]=cmp(y,sa[i-1],sa[i],h)?p-1:p++; 33 } 34 } 35 36 void LCP(int n){ 37 int i,j,k=0; 38 for(i=1;i<=n;i++)rank[sa[i]]=i; 39 for(i=0;i<n;lcp[rank[i++]]=k) 40 for(k?k--:k,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 41 } 42 43 int n; 44 char s[maxn]; 45 46 struct Node{ 47 int x,y,v; 48 Node(int x_=0,int y_=0,int v_=0):x(x_),y(y_),v(v_){} 49 friend bool operator <(Node a,Node b){ 50 return a.v>b.v; 51 } 52 }st[maxn]; 53 int w[maxn],fa[maxn]; 54 long long mx[maxn],mn[maxn]; 55 long long sz[maxn],ans[maxn],sum[maxn]; 56 57 int Find(int x){ 58 return fa[x]==x?x:fa[x]=Find(fa[x]); 59 } 60 61 int main(){ 62 #ifndef ONLINE_JUDGE 63 freopen("savour.in","r",stdin); 64 freopen("savour.out","w",stdout); 65 #endif 66 scanf("%d%s",&n,s); 67 for(int i=0;i<n;i++){ 68 scanf("%d",&w[i]); 69 r[i]=s[i]; 70 } 71 DA(n+1,128);LCP(n); 72 73 for(int i=2;i<=n;i++) 74 st[i-1]=Node(sa[i],sa[i-1],lcp[i]); 75 sort(st+1,st+n); 76 77 for(int i=0;i<n;i++){ 78 fa[i]=i;sz[i]=1; 79 mx[i]=mn[i]=w[i]; 80 sum[i]=-INF; 81 } 82 83 for(int i=st[1].v,j=1;i>=0;i--){ 84 ans[i]=ans[i+1]; 85 sum[i]=sum[i+1]; 86 while(j<n&&st[j].v==i){ 87 int x=Find(st[j].x),y=Find(st[j].y); 88 sum[i]=max(sum[i],mx[x]*mx[y]); 89 sum[i]=max(sum[i],mn[x]*mn[y]); 90 ans[i]+=sz[x]*sz[y]; 91 fa[x]=y;sz[y]+=sz[x]; 92 mn[y]=min(mn[x],mn[y]); 93 mx[y]=max(mx[x],mx[y]); 94 j+=1; 95 } 96 } 97 98 for(int i=0;i<n;i++){ 99 if(ans[i]==0)sum[i]=0; 100 printf("%lld %lld\n",ans[i],sum[i]); 101 } 102 return 0; 103 }
这个是SAM,小了一个log的复杂度,实际上没快多少……
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxn=600010; 6 const long long INF=1LL<<60; 7 int n,cnt,last,ord[maxn],c[maxn]; 8 int fa[maxn],ch[maxn][26],len[maxn]; 9 long long tmp[maxn],mx[maxn],mn[maxn],w[maxn]; 10 long long num[maxn],tot[maxn],t1[maxn],t2[maxn]; 11 char s[maxn]; 12 13 struct SAM{ 14 SAM(){cnt=last=1;} 15 void Insert(int c,int t){ 16 int p=last,np=last=++cnt;tmp[np]=1; 17 len[np]=len[p]+1;mx[cnt]=mn[cnt]=w[t]; 18 while(p&&!ch[p][c])ch[p][c]=np,p=fa[p]; 19 if(!p)fa[np]=1; 20 else{ 21 int q=ch[p][c],nq; 22 if(len[q]==len[p]+1) 23 fa[np]=q; 24 else{ 25 len[nq=++cnt]=len[p]+1; 26 mx[cnt]=-INF;mn[cnt]=INF; 27 memcpy(ch[nq],ch[q],sizeof(ch[q])); 28 fa[nq]=fa[q];fa[q]=fa[np]=nq; 29 while(ch[p][c]==q)ch[p][c]=nq,p=fa[p]; 30 } 31 } 32 return; 33 } 34 35 void Solve(){ 36 for(int i=1;i<=cnt;i++)c[len[i]]++; 37 for(int i=1;i<=len[last];i++)c[i]+=c[i-1]; 38 for(int i=1;i<=cnt;i++)ord[c[len[i]]--]=i; 39 for(int i=1;i<=cnt;i++)t2[i]=num[i]=-INF; 40 mn[1]=INF;mx[1]=-INF; 41 42 for(int i=cnt;i>=1;i--){ 43 int p=ord[i]; 44 t1[fa[p]]+=tmp[fa[p]]*tmp[p]; 45 tmp[fa[p]]+=tmp[p]; 46 if(mn[fa[p]]!= INF)t2[fa[p]]=max(t2[fa[p]],mn[p]*mn[fa[p]]); 47 if(mx[fa[p]]!=-INF)t2[fa[p]]=max(t2[fa[p]],mx[p]*mx[fa[p]]); 48 mn[fa[p]]=min(mn[fa[p]],mn[p]); 49 mx[fa[p]]=max(mx[fa[p]],mx[p]); 50 } 51 52 for(int i=1;i<=cnt;i++){ 53 tot[len[i]]+=t1[i]; 54 num[len[i]]=max(num[len[i]],t2[i]); 55 } 56 57 for(int i=n-2;i>=0;i--){ 58 tot[i]+=tot[i+1]; 59 num[i]=max(num[i],num[i+1]); 60 } 61 62 for(int i=0;i<n;i++) 63 printf("%lld %lld\n",tot[i],tot[i]?num[i]:0); 64 } 65 }sam; 66 67 int main(){ 68 freopen("savour.in","r",stdin); 69 freopen("savour.out","w",stdout); 70 scanf("%d%s",&n,s); 71 for(int i=1;i<=n;i++)scanf("%lld",&w[i]); 72 for(int i=n;i;i--)sam.Insert(s[i-1]-‘a‘,i); 73 sam.Solve(); 74 return 0; 75 }
时间: 2024-09-29 23:26:59