标签:
这题的题意真是难于理解
意思就是挨个放单词,放每个单词之前一定把这个单词的后缀都先放上去,每个单词的代价等于这个单词的位置减去上一个出现的这个单词的后缀的位置
第一个条件是没用的,因为如果触发这个条件一定不是最优的
然后嘛,我们把串反一下,建一颗trie,把除了根意外不是作为串的结尾的没用的点去掉,这样就变成了一颗树,问题转化为给树上每个点标号,每个点的标号大于其父亲的编号,每个点代价为他的标号减去其父亲标号,最小化代价
贪心,dfs并先走siz小的儿子进行标号即可,证明如下
感性证明:手玩一下觉得是dfs序,dfs序的话按siz贪心显然
接下来理性证明
首先我们证明二叉菊花情况
根节点代价为0,对于非根节点x,代价为1+选fa[x]到选x之间选的点的数量,对所有点的代价作sigma,变为n-1+sigma选fa[x]到选x之间选的点的数量
n-1常量去掉,因为是二叉菊花,所以等价为选第一个叉的叶子之前选的第二个叉上点的数量+选第二个叉的叶子之前选的第一个叉上点的数量因为其中一个一定是最后一个选的,所以一定是先把小的那一叉选完为最优,这样是一个
二叉菊花证完后x叉菊花可视为x-1叉菊花接一个叉,x-1叉叉内的相对顺序已知,作类似证明可知一定先把x-1叉和新叉里小的那一个走完
把菊花上边接一条链,相当于菊花,然后再把这样的东西拼一起,作类似证明,就推广到了树
证毕
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<iomanip> #include<vector> #include<stack> #include<queue> #include<map> #include<set> #include<bitset> using namespace std; #define MAXN 100010 #define MAXM 510010 #define ll long long #define INF 1000000000 #define MOD 1000000007 #define eps 1e-8 int son[MAXM][26]; char s[MAXM]; bool v[MAXM]; int tms[MAXM]; int tim; int rt,tot; int n; ll ans; vector<int>mp[MAXN]; int siz[MAXN]; bool cmp(int x,int y){ return siz[x]<siz[y]; } void ins(){ int l=strlen(s+1); int p=rt; int i; for(i=l;i;i--){ if(!son[p][s[i]-'a']){ son[p][s[i]-'a']=++tot; } p=son[p][s[i]-'a']; } v[p]=1; } void dfs(int x){ int i; if(v[x]){ mp[tms[x]].push_back(++tim); tms[x]=tim; } for(i=0;i<26;i++){ if(son[x][i]){ tms[son[x][i]]=tms[x]; dfs(son[x][i]); } } } void dp(int x){ int i,y; siz[x]=1; for(i=0;i<mp[x].size();i++){ y=mp[x][i]; dp(y); siz[x]+=siz[y]; } } void cal(int x){ int i; tms[x]=++tim; sort(mp[x].begin(),mp[x].end(),cmp); for(i=0;i<mp[x].size();i++){ ans+=tim+1-tms[x]; cal(mp[x][i]); } } int main(){ int i; rt=tot=1; scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%s",s+1); ins(); } tms[rt]=tim=1; dfs(rt); dp(1); tim=0; cal(1); printf("%lld\n",ans); return 0; } /* 2 a ba */
标签:
原文地址:http://blog.csdn.net/neither_nor/article/details/51362523