[NOI2015]品酒大會(字尾樹+DP)
阿新 • • 發佈:2019-01-03
字尾自動機有一個性質。就是兩個串的lcp就是這兩個點的結束節點的LCA對應字首。
然後就可以愉快的跑DP了。
對於每一個字尾樹上的節點\(u\),它對\(len[u]\)的貢獻是\(\sum_{v1}\sum_{v2\neq{v1}}size[v1]*size[v2]\)當然如果u就是一個字尾的結尾就要加上自己。
然後最大值怎麼辦?我們在每一個節點上維護最小次小,最大次大然後DP轉移就行。
因為這題\(k\)相似就是\(k-1\)相似。最後還要對個數求個和,對最大值求一個\(max\)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define int long long const int N=601000; const int INF=1e18+10; int cnt,head[N]; int tot=1,u=1,trans[N][27],size[N],len[N],fa[N]; int mx[N],mxx[N],mn[N],mnn[N],dp[N]; int ans[N],anss[N],n,a[N]; char s[N]; struct edge{ int to,nxt; }e[N]; void add(int u,int v){ cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'||ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } void ins(int id,int c){ int x=++tot;len[x]=len[u]+1; mx[x]=mn[x]=a[id];size[x]=1;mxx[x]=-INF;mnn[x]=INF; for(;u&&trans[u][c]==0;u=fa[u])trans[u][c]=x; if(u==0)fa[x]=1; else{ int v=trans[u][c]; if(len[u]+1==len[v])fa[x]=v; else{ int w=++tot;len[w]=len[u]+1; mx[w]=-INF;mn[w]=INF;mxx[x]=-INF;mnn[x]=INF; memcpy(trans[w],trans[v],sizeof(trans[w])); fa[w]=fa[v]; fa[x]=fa[v]=w; for(;u&&trans[u][c]==v;u=fa[u])trans[u][c]=w; } } u=x; } void dfs(int u){ int tmp=size[u]; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; dfs(v); dp[u]+=size[v]*tmp; tmp+=size[v]; if(mx[v]>mx[u]){ mxx[u]=mx[u]; mx[u]=mx[v]; if(mxx[v]>mxx[u])mxx[u]=mxx[v]; } else if(mx[v]>mxx[u])mxx[u]=mx[v]; if(mn[v]<mn[u]){ mnn[u]=mn[u]; mn[u]=mn[v]; if(mnn[v]<mnn[u])mnn[u]=mnn[v]; } else if(mn[v]<mnn[u])mnn[u]=mn[v]; size[u]+=size[v]; } ans[len[u]]+=dp[u]; if(size[u]!=1)anss[len[u]]=max(anss[len[u]],max(mx[u]*mxx[u],mn[u]*mnn[u])); } signed main(){ scanf("%lld",&n); scanf("%s",s+1); for(int i=1;i<=n;i++)scanf("%lld",&a[i]); for(int i=n;i>=1;i--)ins(i,s[i]-'a'+1); for(int i=1;i<=tot;i++)add(fa[i],i); for(int i=0;i<=n+1;i++)anss[i]=-INF; dfs(1); for(int i=n;i>=0;i--)ans[i]+=ans[i+1],anss[i]=max(anss[i],anss[i+1]); for(int i=0;i<n;i++) if(ans[i]==0)printf("0 0\n"); else printf("%lld %lld\n",ans[i],anss[i]); return 0; }