您的位置:首页 > 其它

BZOJ 4567: [Scoi2016]背单词

2016-05-23 19:21 435 查看
显然第一种情况可以避免

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

这棵树可以通过每个串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
[26],val
,sz;
int siz
;
vector<int>son
;
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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: