bzoj4567 [Scoi2016]背单词

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

题解:
   
题目是可以贪心的,刚开始的时候想抄发题解,发现风格不同,算了,自己打吧。
    然后自己瞎搞了半天,首先,n*n的那个是绝对不可以直接选的,这个选了,那是
    要爆炸的,然后考虑两种,应该后缀先加入,这个方向思考,可以发现,应该可以
    转化为图论,应该是最短后缀先背,然后次短,...,到达该单词为止。
    这里可以用字典树预处理出来,就是构建图出来,变成了多个联通块,这时该怎么办
    发现,这是多个DAG,就是拓扑图,然后建立一个虚根,来连接这些点。
    

      很明显右边那种不行,不可能两种长度一样的后缀,因为题目保证给定单词两两不同。

      然后就是一棵树了,并且将问题进一步转化,每个点的花费,就是其儿子节点编号减父亲

      节点编号,这样怎么样最小,可以证明,每次优先编号size小的子树,这样花费最小。

      

      挺好证明的吧,然后就ok了,dfs一次就好了。

  1 #include<cstring>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstdio>
  6 #include<queue>
  7 #include<vector>
  8 #define N 100007
  9 #define LEN 510007
 10 #define fzy pair<int,int>
 11 #define ll long long
 12 using namespace std;
 13 
 14 int n;
 15 int ckt,top=1,bh[N],siz[N],du[N];
 16 ll ans;
 17 int cnt,head[N],next[N],rea[N];
 18 vector<int>ch[N];
 19 char s[LEN];
 20 struct Node
 21 {
 22     int point[26],flag;
 23 }trie[LEN*2];
 24 
 25 void insert(int x)
 26 {
 27     int now=1,len=ch[x].size()-1;
 28     for (int i=len;i>=0;i--)
 29     {
 30         if (!trie[now].point[ch[x][i]])
 31         {
 32             trie[now].point[ch[x][i]]=++top;
 33             now=top;
 34         }
 35         else now=trie[now].point[ch[x][i]];
 36     }
 37     trie[now].flag=x;
 38 }
 39 void add(int u,int v)
 40 {
 41     next[++cnt]=head[u];
 42     head[u]=cnt;
 43     rea[cnt]=v;
 44     du[v]++;
 45 }
 46 void solve_make(int x)
 47 {
 48     int now=1,len=ch[x].size()-1,id=-1;
 49     for (int i=len;i>=0;i--)
 50     {
 51         if (trie[now].flag) id=trie[now].flag;
 52         now=trie[now].point[ch[x][i]];
 53     }
 54     if (id!=-1&&id!=x) add(id,x);
 55 }
 56 void dfs_init(int u,int fa)
 57 {
 58     siz[u]=1;
 59     for (int i=head[u];i!=-1;i=next[i])
 60     {
 61         int v=rea[i];
 62         if (v==fa) continue;
 63         dfs_init(v,u);
 64         siz[u]+=siz[v];
 65     }
 66 }
 67 void dfs_solve(int u,int fa)
 68 {
 69     priority_queue<fzy,vector<fzy>,greater<fzy> >q;
 70     while(!q.empty()) q.pop();
 71     bh[u]=++ckt;
 72     ans+=bh[u]-bh[fa];
 73     for (int i=head[u];i!=-1;i=next[i])
 74     {
 75         int v=rea[i];
 76         if (v==fa) continue;
 77         q.push(make_pair(siz[v],v));
 78     }
 79     while(!q.empty())
 80     {
 81         int now=q.top().second;
 82         dfs_solve(now,u);
 83         q.pop();
 84     }
 85 }
 86 int main()
 87 {
 88     memset(head,-1,sizeof(head));
 89     scanf("%d",&n);
 90     for (int i=1;i<=n;i++)
 91     {
 92         scanf("%s",s);
 93         for (int j=0;j<strlen(s);j++)
 94             ch[i].push_back((int)(s[j]-'a'));
 95         insert(i);
 96     }
 97     for (int i=1;i<=n;i++)
 98         solve_make(i);
 99     int S=n+1;
100     for (int i=1;i<=n;i++)
101         if (du[i]==0) add(S,i);
102     dfs_init(S,S);
103     dfs_solve(S,S);    
104     printf("%lld\n",ans);
105 }

 

  
posted @ 2017-10-25 21:13  Kaiser-  阅读(398)  评论(0编辑  收藏  举报