zoukankan      html  css  js  c++  java
  • [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 }
  • 相关阅读:
    POJ
    HDU
    Python之列表
    列表、元组、字典总结
    Python之列表、原组、字典总结
    [P1082][NOIP2012] 同余方程 (扩展欧几里得/乘法逆元)
    [P3957][NOIP2017]跳房子 (DP+二分/队列?)
    [Codeforces896C] Willem, Chtholly and Seniorious (ODT-珂朵莉树)
    [P1005][NOIP2007] 矩阵取数游戏 (DP+高精)
    [POJ1006]生理周期 (中国剩余定理)
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7660412.html
Copyright © 2011-2022 走看看