BZOJ 4567: [Scoi2016]背单词

来源:互联网 发布:linux云计算视频教程 编辑:程序博客网 时间:2024/06/05 17:30

显然第一种情况可以避免

将每个串都看成树上的一个节点,父亲为它的后缀串中最长的那个

这棵树可以通过每个串reverse后加入Trie树中,最后去掉Trie树的虚节点来获得

于是问题变成了给树上每个点标号,使得每个点的标号减去它父亲的标号的和最小

显然要按DFS序标号

考虑相邻的兄弟节点u,v

先u再v比先v再u的答案大siz[u]-siz[v]

所以子节点按子树大小排序后依次标号即可

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<queue>#include<vector>#include<algorithm>#include<map>#include<set>#include<stack>#define rep(i,l,r) for(int i=l;i<=r;i++)#define per(i,r,l) for(int i=r;i>=l;i--)#define mmt(a,v) memset(a,v,sizeof(a))#define tra(i,u) for(int i=head[u];i;i=e[i].next)using namespace std;typedef long long ll;const int N=510000+5;int ch[N][26],val[N],sz;int siz[N];vector<int>son[N];void insert(char *s,int k){int u=0,n=strlen(s+1);reverse(s+1,s+1+n);rep(i,1,n){int c=s[i]-'a';if(!ch[u][c])ch[u][c]=++sz;u=ch[u][c];}val[u]=k;}void dfs(int u,int fa){if(val[u])son[fa].push_back(val[u]),fa=val[u];rep(i,0,25)if(ch[u][i])dfs(ch[u][i],fa);}bool cmp(int i,int j){return siz[i]>siz[j];}int dfs(int u){siz[u]=1;per(i,(int)son[u].size()-1,0)siz[u]+=dfs(son[u][i]);sort(son[u].begin(),son[u].end(),cmp);return siz[u];}int id;ll solve(int u,int fa){ll ans=0;ans+=id-fa;fa=id++;per(i,(int)son[u].size()-1,0)ans+=solve(son[u][i],fa);return ans;}char s[N];int main(){//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);int n;scanf("%d",&n);rep(i,1,n)scanf("%s",s+1),insert(s,i);dfs(0,0);dfs(0);printf("%lld\n",solve(0,0));return 0;}


0 0