[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 吃的最少泡椒数
输入输出样例
2 a ba
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]背单词的主要内容,如果未能解决你的问题,请参考以下文章