[BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机

由于要找后缀的前缀,所以先用反串建立SAM。
link边组成了后缀树。
两个子串的最长公共前缀是LCA的step
树形dp即可。
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define maxn 1200000
 8 using namespace std;
 9 struct edge {
10     int to,next;
11 }e[maxn];
12 int n,a[maxn];
13 char s[maxn];
14 int head[maxn],d;
15 long long ans[maxn],mx[maxn],mn[maxn],sum[maxn];
16 struct data {
17     int last,cnt;
18     int son[maxn][26],link[maxn],step[maxn],v[maxn],size[maxn];
19     int tmp[maxn];
20     data() {last=cnt=1;v[1]=2147483647;}
21     void extend(int c,int val) {
22         int p=last,np=last=++cnt;step[np]=step[p]+1;v[np]=val,size[np]=1;
23         while(p&&!son[p][c]) son[p][c]=np,p=link[p];
24         if(!p) link[np]=1;
25         else {
26             int q=son[p][c];
27             if(step[q]==step[p]+1) link[np]=q;
28             else {
29                 int nq=++cnt;v[nq]=2147483647;
30                 memcpy(son[nq],son[q],sizeof(son[q]));
31                 link[nq]=link[q];
32                 link[q]=link[np]=nq;
33                 step[nq]=step[p]+1;
34                 while(son[p][c]==q&&p) son[p][c]=nq,p=link[p];
35             }
36         }
37     }
38     void add(int u,int v) {e[d].next=head[u];e[d].to=v;head[u]=d++;}
39     void build() {for(int i=1;i<=cnt;i++) add(link[i],i),ans[i]=-21474836470000000000000LL;}
40     void query(int x) {
41         mx[x]=-2147483647;mn[x]=2147483647;
42         if(v[x]!=2147483647) mx[x]=mn[x]=v[x];
43         for(int i=head[x];i>=0;i=e[i].next) {
44             int to=e[i].to;
45             query(to);
46             if(mx[x]!=-2147483647&&mn[x]!=2147483647&&mx[to]!=-2147483647&&mn[to]!=2147483647) ans[step[x]]=max(ans[step[x]],max(mx[x]*mx[to],mn[to]*mn[x]));
47             mx[x]=max(mx[x],mx[to]);mn[x]=min(mn[x],mn[to]);
48             sum[step[x]]+=1LL*size[x]*size[to];size[x]+=size[to];
49         }
50         
51     }
52     void getans() {
53         for(int i=n-1;i>=0;i--) sum[i]+=sum[i+1],ans[i]=max(ans[i],ans[i+1]); 
54         for(int i=0;i<=n-1;i++) if(sum[i]) printf("%lld %lld\n",sum[i],ans[i]); else printf("0 0\n");
55     }
56     
57 }sam;
58 
59 int main() {
60     memset(head,-1,sizeof(head));
61     scanf("%d",&n);
62     scanf("%s",s+1);
63     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
64     for(int i=n;i>=1;i--) sam.extend(s[i]-'a',a[i]);
65     sam.build();
66     sam.query(1);
67     sam.getans();
68     
69 }
View Code

 

posted @ 2018-01-10 19:34  wls001  阅读(178)  评论(0编辑  收藏  举报