給定一個長度爲 \(n\) 的字符串 \(s\), 對於全部 \(r\in[1,n]\) 求出 \(s\) 的全部LCP不小於 \(r\) 的後綴對的個數以及這些後綴對所能組成的最大權值.php
一個後綴對 \((a,b)\) 的權值是它們左端點的權值的積.c++
\(n\le 3\times 10^5\).spa
好久之前寫的SAM沙雕題code
由於要求LCP因此咱們把這個串reverse一下用SAM搞.blog
根據後綴自動機的性質, 某兩個後綴的LCP就是它們在SAM上對應結點的LCA的 \(len\).字符串
那麼對於計數的部分, 咱們顯然只要對於每一個點都算出有多少個後綴以它爲LCA就能夠了.get
後面求最大權值的部分看上去好像只要記錄一會兒樹中的最大值和次大值就能夠了, 然而權值可能有負數因而還得記錄最小值和次小值.it
計算出每一個 \(len\) 的貢獻後取後綴和就能夠出答案了.ast
#include <bits/stdc++.h> const int MAXN=6e5+10; typedef long long int64; struct Edge{ int from; int to; Edge* next; }; Edge E[MAXN]; Edge* head[MAXN]; Edge* top=E; int n; int cnt=1; int root=1; int last=1; int v[MAXN]; char s[MAXN]; int len[MAXN]; int prt[MAXN]; int val[MAXN]; int size[MAXN]; int64 ans[MAXN]; int64 sum[MAXN]; int maxv[MAXN][2]; int minv[MAXN][2]; std::map<char,int> chd[MAXN]; void DFS(int); void Insert(int,int); void Extend(char,int); int main(){ memset(ans,0x80,sizeof(ans)); memset(maxv,0x80,sizeof(maxv)); memset(minv,0x7F,sizeof(minv)); scanf("%d",&n); scanf("%s",s); for(int i=0;i<n;i++) scanf("%d",v+i); for(int i=1;i<=n;i++) Extend(s[n-i],v[n-i]); for(int i=2;i<=cnt;i++) Insert(prt[i],i); DFS(root); for(int i=n-1;i>=0;i--){ sum[i]+=sum[i+1]; ans[i]=std::max(ans[i],ans[i+1]); } for(int i=0;i<n;i++) printf("%lld %lld\n",sum[i],sum[i]==0?0:ans[i]); return 0; } void UpdateMax(int x,int v){ maxv[x][1]=std::max(maxv[x][1],v);; if(maxv[x][0]<maxv[x][1]) std::swap(maxv[x][0],maxv[x][1]); } void UpdateMin(int x,int v){ minv[x][1]=std::min(minv[x][1],v); if(minv[x][0]>minv[x][1]) std::swap(minv[x][0],minv[x][1]); } void DFS(int root){ for(Edge* i=head[root];i!=NULL;i=i->next){ DFS(i->to); sum[len[root]]+=1ll*size[root]*size[i->to]; size[root]+=size[i->to]; UpdateMin(root,minv[i->to][0]); UpdateMin(root,minv[i->to][1]); UpdateMax(root,maxv[i->to][0]); UpdateMax(root,maxv[i->to][1]); } if(size[root]>1) ans[len[root]]=std::max(ans[len[root]],std::max(1ll*minv[root][0]*minv[root][1],1ll*maxv[root][0]*maxv[root][1])); } void Extend(char x,int v){ int p=last; int np=++cnt; size[last=np]=1; len[np]=len[p]+1; minv[np][0]=v; maxv[np][0]=v; while(p&&!chd[p].count(x)) chd[p][x]=np,p=prt[p]; if(p==0) prt[np]=root; else{ int q=chd[p][x]; if(len[q]==len[p]+1) prt[np]=q; else{ int nq=++cnt; chd[nq]=chd[q]; prt[nq]=prt[q]; prt[q]=nq; prt[np]=nq; len[nq]=len[p]+1; while(p&&chd[p][x]==q) chd[p][x]=nq,p=prt[p]; } } } void Insert(int from,int to){ top->from=from; top->to=to; top->next=head[from]; head[from]=top++; }