【BZOJ3238】【AHOI2013】差异

sam好,好写好调好ac!

原题:

图片题面好评

2<=N<=500000

 

在syq大神的指点下终于理解一道后缀自动姬了quq

(其实是因为这道题的dp主要是在后缀树(就是拓扑序)上搞树形dp……

恩sam有个好玩的东西呢就是搞出后缀自动姬后根据max搞一个类似与后缀数组中countrank的东西

这个就是自动姬的拓扑序,同时也是parent树的不知道什么序,反正如果倒叙遍历这个序列的话x一定会比father[x]先访问到就对了

然后就可以直接用countrank搞树形dp辣

每个树点对答案的贡献就是(max[x]-max[father[x]])*C_{|right[x]|}^{2}

写到这里我突然发现这个组合数不太理解啊,如果两个节点在同一个子节点的子树中怎么办……

一定是还有什么性质我没考虑到

syq回寝吃泡面了,只能回去问syq了quq

啊,syq吃完泡面后讲明白了quq

就像下面酱紫一个图:

在这个后缀树中,现在计算2节点对于答案的贡献

我本来的想法是如果直接用2的深度乘C_{子树大小}^{2}岂不是会出现两个节点在同一子节点的子树中然后重复计算的情况?
但是实际上在计算贡献的时候是用(max[x]-max[father[x]])乘组合数的,这个表示的是2和1之间的连边,而不是2的深度

2和1对答案的贡献显然就乘C_{子树大小}^{2}

这样就解决了Σlcp(i,j)*2的问题,至于前面那些东西,最后结果是(n+1)*(n-1)*n/2,请同学们自行推到 _(:3 」∠)_

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define ll long long
 8 int rd(){int z=0,mk=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mk;
12 }
13 char s[510000];  int n;
14 int nxt[1100000][26],fa[1100000],mx[1100000],sz[1100000],sm[1100000];
15 int lst=1,tt=1;
16 int cnt[1100000],cntrk[1100000];
17 void ist(int x){
18     int p=lst,np=lst=++tt;
19     mx[np]=mx[p]+1;  sz[np]=sm[np]=1;
20     while(!nxt[p][x] && p)  nxt[p][x]=np,p=fa[p];
21     if(!p)  fa[np]=1;
22     else{
23         int q=nxt[p][x];
24         if(mx[p]+1==mx[q])  fa[np]=q;
25         else{
26             int nq=++tt;  mx[nq]=mx[p]+1;
27             memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
28             fa[nq]=fa[q];  fa[q]=fa[np]=nq;
29             while(nxt[p][x]==q)  nxt[p][x]=nq,p=fa[p];
30         }
31     }
32 }
33 void gtcntrk(){
34     for(int i=1;i<=tt;++i)  ++cnt[mx[i]];
35     for(int i=1;i<=n;++i)  cnt[i]+=cnt[i-1];
36     for(int i=tt;i;--i)  cntrk[cnt[mx[i]]--]=i;
37 }
38 ll play(){
39     ll bwl=0;
40     for(int i=tt;i;--i){
41         sz[fa[cntrk[i]]]+=sz[cntrk[i]];
42         /*bwl+=(ll)sm[fa[cntrk[i]]]*sz[cntrk[i]]*mx[fa[cntrk[i]]];
43         sm[fa[cntrk[i]]]+=sz[cntrk[i]];*/
44         bwl+=(ll)(mx[cntrk[i]]-mx[fa[cntrk[i]]])*sz[cntrk[i]]*(sz[cntrk[i]]-1);
45     }
46     return bwl;
47 }
48 int main(){//freopen("ddd.in","r",stdin);
49     scanf("%s",s+1);  n=strlen(s+1);
50     for(int i=1;i<=n;++i)  ist(s[i]-'a');
51     gtcntrk();
52     cout<<(ll)(n+1)*n/2*(n-1)-play()<<endl;
53     return 0;
54 }
View Code

 

转载于:https://www.cnblogs.com/JSL2018/p/6545370.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值