[SCOI2016]背单词

Posted Z-Y-Y-S

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2016]背单词相关的知识,希望对你有一定的参考价值。

题目描述

Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:

—————序号 单词—————

1 2......n-2n-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 个单词的情况下,吃最少的泡椒。

输入输出格式

输入格式:

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

输出格式:

Lweb 吃的最少泡椒数

输入输出样例

输入样例#1:
2
a
ba
输出样例#1:
2

我们将“后缀”转化为“前缀”,那么很显然这是一个可以在$Trie$树上解决的问题。

我们建完$Trie$树后,我们建立一个根节点$0$,发现其实答案就是所有节点的编号与其父亲节点的编号差值和。

这个结论是建立在$1$号情况不能出现的情况下,因为$1$号情况代价最大,我们显然必须避开,并且一定能够避开。

那么现在问题就变成了给你一棵树,让你给节点编号(父节点编号一定比子节点小),求所有节点的编号与其父亲节点的编号差值和最小值。

很容易得到的一个贪心策略是:每次选深度最小的子树先标号。

简要证明一下:因为对于一个子树,其内部最优值一定是不变的,影响答案的只有与当前节点相邻节点的编号关系。那么我们一定先选子树$size$小的先编号,一定能得到最优值。

那么$dfs$的时候我们要选最小的$size$,如何实现?我们可以开个栈来按顺序存储要访问的节点,就可以了。

菜鸡附上转载地址:http://www.cnblogs.com/NaVi-Awson/p/7648048.html

%%%%SAC

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<stack>
 6 using namespace std;
 7 typedef long long ll;
 8 const int M=550010;
 9 const int N=200010;
10 struct Node
11 {
12   int next,to;
13 }edge[N+5];
14 ll ans;
15 int vis[N+5],n;
16 char s[M+5];
17 int trie[M+5][26],pd[M+5],pos,num,head[N+5],size[N+5],tot,tmp[N+5];
18 stack<int>S;
19 void add(int u,int v)
20 {
21   num++;
22   edge[num].next=head[u];
23   head[u]=num;
24   edge[num].to=v;
25 }
26 void insert(char *s,int len,int id)
27 {int i;
28   int x=0;
29   for (i=len-1;i>=0;i--)
30     {
31       if (!trie[x][s[i]-\'a\']) trie[x][s[i]-\'a\']=++pos;
32       x=trie[x][s[i]-\'a\'];
33     }
34   pd[x]=id;
35 }
36 void dfs(int x,int last)
37 {int i;
38   if (pd[x]) add(last,pd[x]),last=pd[x];
39   for (i=0;i<26;i++)
40     if (trie[x][i])
41       dfs(trie[x][i],last);
42 }
43 void get_size(int x)
44 {int i;
45   size[x]=1;
46   for (i=head[x];i;i=edge[i].next)
47     {
48       int v=edge[i].to;
49       get_size(v);
50       size[x]+=size[v];
51     }
52 }
53 bool cmp(int x,int y)
54 {
55   return size[x]<size[y];
56 }
57 void get_ans(int x,int fa)
58 {int i;
59   int top=0;
60   vis[x]=++tot;
61   ans+=vis[x]-vis[fa];
62   for (i=head[x];i;i=edge[i].next)
63     {
64       int v=edge[i].to;
65       tmp[++top]=v;
66     }
67   sort(tmp+1,tmp+top+1,cmp);
68   for (i=top;i>=1;i--)
69     S.push(tmp[i]);
70   while (top--)
71     {
72       i=S.top();
73       S.pop();
74       get_ans(i,x);
75     }
76 }
77 int main()
78 {int i;
79   cin>>n;
80   for (i=1;i<=n;i++)
81     {
82       scanf("%s",s);
83       insert(s,strlen(s),i);
84     }
85   dfs(0,0);
86   get_size(0);
87   get_ans(0,0);
88   cout<<ans;
89 }

 

以上是关于[SCOI2016]背单词的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ #2012. 「SCOI2016」背单词

bzoj4567SCOI2016背单词

BZOJ4567: [Scoi2016]背单词

P3294 [SCOI2016]背单词

BZOJ4567[Scoi2016]背单词 Trie树+贪心

[SCOI2016]背单词