BZOJ4567 背单词

来源:互联网 发布:图片制作拼图软件 编辑:程序博客网 时间:2024/05/17 09:38


Description

Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”。这时候睿智
的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:
—————
序号  单词
—————
 1
 2
……
n-2
n-1
 n
—————
然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 
的单词(序号 1...x-1 都已经被填入):
1) 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 n×n 颗泡椒才能学会;
2) 当它的所有后缀都被填入表内的情况下,如果在 1...x-1 的位置上的单词都不是它的后缀,那么你吃 x 颗泡
椒就能记住它;
3) 当它的所有后缀都被填入表内的情况下,如果 1...x-1的位置上存在是它后缀的单词,所有是它后缀的单词中
,序号最大为 y ,那么你只要吃 x-y 颗泡椒就能把它记住。
Lweb 是一个吃到辣辣的东西会暴走的奇怪小朋友,所以请你帮助 Lweb ,寻找一种最优的填写单词方案,使得他
记住这 n 个单词的情况下,吃最少的泡椒。

Input

输入一个整数 n ,表示 Lweb 要学习的单词数。接下来 n 行,每行有一个单词(由小写字母构成,且保证任意单
词两两互不相同)1≤n≤100000, 所有字符的长度总和 1≤|len|≤510000

Output

 Lweb 吃的最少泡椒数

Sample Input

2
a
ba

Sample Output

2

HINT

Source


本题的题意很绕啊!!!(SB出题人)
大意就是给你一堆单词,自己规定顺序排入
(1)条件是没用的,因为如果有后缀存在,我们就直接应用第2,3个条件了
PS:您会发现"当它的所有后缀都被填入表内的情况下"不是说它的"所有"后缀,而是在那一堆单词里存在的是它后缀的单词

于是我们就逆序建个trie,就考虑一下贪心,即dfs时每次先向单词少的地方走
具体实现时,就要注意一下计算答案的过程
只有在一个单词节点或根节点时才统计答案,所以可以维护一个栈,把每个单词节点子树的答案push进去。。。xjb乱搞

#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>using namespace std;int trie[510001][26],word[510001*26],tsz;bool isword[510001*26];char str[100100];  struct data{    int u,sz;    data(){u=sz=0;}    data(int u,int sz):u(u),sz(sz){}    int operator<(const data& d)const{        return sz<d.sz;      }};int q[500100*26],qr;void insert(){    int len=strlen(str);    int rt=0;    for(int i=len-1;i>=0;--i){        if(!trie[rt][str[i]-'a'])            trie[rt][str[i]-'a']=++tsz;        rt=trie[rt][str[i]-'a'];    }    isword[rt]=1;}void dfs2(int u){    if(word[u])return ;    word[u]=isword[u];    for(int i=0;i<26;++i)if(trie[u][i])        dfs2(trie[u][i]),word[u]+=word[trie[u][i]];}long long dfs(int u){    int nr=qr;    data d[26];    long long ans=0;    for(int i=0;i<26;++i)if(trie[u][i])        dfs2(trie[u][i]),d[i]=data(trie[u][i],word[trie[u][i]]);    sort(d,d+26);    for(int i=0;i<26;++i)if(d[i].u)        ans+=dfs(d[i].u);    long long t=1;    if(isword[u]&&nr<qr){        sort(q+nr+1,q+qr+1);        for(int i=nr+1;i<=qr;++i){            ans+=t;t+=q[i];        }           qr=nr;    }    if(isword[u])q[++qr]=word[u];    return ans;}int main(){    int n;scanf("%d",&n);    for(int i=1;i<=n;++i){        scanf("%s",str);        insert();    }    isword[0]=1;    printf("%lld",dfs(0));}


By yfzcsc
0 0