题目链接:http://uoj.ac/problem/131
题意:给出一个字符串,第i个字符对应的值为a[i], 对于i∈[0,n),求最长公共前缀大于等于i的字串对个数,并求这些字符串对开头对应值相乘最大值。n=3*10^5
题解:
学了个厉害的东西啊。。。
正解好像是sa+并查集(合并height)
然而我学了个用sam的做法。。
对于第一问:
首先我们要知道,建立后缀自动机之后,parent树就是逆序串的后缀树。
why?看这个博客好了:http://z55250825.blog.163.com/blog/static/15023080920144542541495/
直接逆序建后缀自动机,
因为对于现在parent树而言,任意两点的LCP等于两点在树上的LCA的step(step就是sam里的那个step。。一开始没想清楚还以为是parent-tree上的深度。。于是WA了。。)
这是转化成一个简单的树形dp了:按逆拓扑序更新(从孩子到parent),对于当前点x,看它是多少对点对的lcp。
假设有四个孩子,孩子的点数(就是这棵子树上有多少个点)分别为s1,s2,s3,s4
cnt[x]=1*(s1+s2+s3+s4)(这是x到x的孩子) + (s1+s2+s3)*s4 + (s1+s2)*s3 + s1*s2
那我们每遍历一个孩子y,就sum[x]+=sum[y],对于一个新的孩子yy,cnt[x]+=sum[x]*sum[yy];
对于第二问:
对于当前的parent树而言,等价于求parent树上两个叶节点乘积的最大值。
又因为考虑到ai可能是负数,所以我们只需要记录最大值,次大值,最小值,次小值就可以了。
参考题解:http://www.cnblogs.com/joyouth/p/5366396.html
注意很多细节。。
sam真的超厉害。。可以直接转化成后缀树和后缀数组。。
ORZ。。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<queue> using namespace std; typedef long long LL; const int N=2*3*100010; const LL INF=1LL<<62; int sl,cl,tot,last,c[N],in[N],first[N],step[N],pre[N],son[N][30]; LL w[N],cnt[N],ans[N],mx[N],smx[N],mn[N],smn[N],sum[N]; char s[N]; bool vis[N]; queue<int> Q; LL maxx(LL x,LL y){return x>y ? x:y;} LL minn(LL x,LL y){return x<y ? x:y;} void gmax(LL &x,LL y){x=maxx(x,y);} void gmin(LL &x,LL y){x=minn(x,y);} int add_node(int x) { step[++tot]=x; return tot; } void clear() { memset(son,0,sizeof(son)); memset(pre,0,sizeof(pre)); memset(step,0,sizeof(step)); memset(in,0,sizeof(in)); // memset(cnt,0,sizeof(cnt)); memset(sum,0,sizeof(sum)); tot=0;add_node(0);last=1; } void extend(int ch) { int p=last,np=add_node(step[p]+1); while(p && !son[p][ch]) { son[p][ch]=np; in[np]++; p=pre[p]; } if(!p) pre[np]=1; else { int q=son[p][ch]; if(step[q]==step[p]+1) pre[np]=q; else { int nq=add_node(step[p]+1); for(int i=1;i<=26;i++) if(son[q][i]) son[nq][i]=son[q][i],in[son[q][i]]++; pre[nq]=pre[q]; pre[np]=pre[q]=nq; while(p && son[p][ch]==q) in[q]--,in[nq]++,son[p][ch]=nq,p=pre[p]; } } last=np; } void get_tp() { while(!Q.empty()) Q.pop(); memset(vis,0,sizeof(vis)); Q.push(1);vis[1]=1;cl=0; while(!Q.empty()) { int x=Q.front();c[++cl]=x;vis[x]=0;Q.pop(); for(int i=1;i<=26;i++) { int y=son[x][i]; if(!y) continue; in[y]--; if(!in[y] && !vis[y]) vis[y]=1,Q.push(y); } } } int main() { freopen("a.in","r",stdin); int x,y,ch; scanf("%d",&sl); scanf("%s",s+1); for(int i=1;i<=sl;i++) scanf("%lld",&w[i]); clear(); for(int i=sl;i>=1;i--) extend(s[i]-‘a‘+1); get_tp(); for(int i=1;i<=tot;i++) mx[i]=-INF,smx[i]=-INF,mn[i]=INF,smn[i]=INF; x=1; for(int i=sl;i>=1;i--) { ch=s[i]-‘a‘+1; x=son[x][ch]; mx[x]=mn[x]=w[i]; sum[x]++; } LL tmp; memset(cnt,0,sizeof(cnt)); for(int i=0;i<=sl;i++) ans[i]=-INF; for(int i=cl;i>=1;i--) { y=c[i],x=pre[y]; tmp=-INF; if(smx[y]>-INF) gmax(tmp,mx[y]*smx[y]); if(smn[y]<INF) gmax(tmp,mn[y]*smn[y]); gmax(ans[step[y]],tmp); cnt[step[x]]+=sum[x]*sum[y]; sum[x]+=sum[y]; if(mx[y]>=mx[x]) smx[x]=mx[x],mx[x]=mx[y];//debug >= else if(mx[y]>smx[x]) smx[x]=mx[y]; if(mn[y]<=mn[x]) smn[x]=mn[x],mn[x]=mn[y];//debug <= else if(mn[y]<smn[x]) smn[x]=mn[y]; } // for(int i=0;i<sl;i++) printf("x = %d cnt = %lld ans = %lld\n",i,cnt[i],ans[i]); for(int i=sl-1;i>=0;i--) { cnt[i]+=cnt[i+1]; gmax(ans[i],ans[i+1]); } for(int i=0;i<sl;i++) { if(!cnt[i]) ans[i]=0; printf("%lld %lld\n",cnt[i],ans[i]); } return 0; }