zoukankan      html  css  js  c++  java
  • [SCOI 2016]背单词

    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

    题目大意

    给你$n$个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串$s$的位置为$x$):
    1.排在$s$后面的字符串有$s$的后缀,则代价为$n^2$;
    2.排在$s$前面的字符串有$s$的后缀,且没有排在$s$后面的$s$的后缀,则代价为$x-y$($y$为最后一个与$s$不相等的后缀的位置);
    3.$s$没有后缀,则代价为$x$。
    求最小代价和。

    题解

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

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

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

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

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

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

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

     1 //It is made by Awson on 2017.10.10
     2 #include <set>
     3 #include <map>
     4 #include <cmath>
     5 #include <ctime>
     6 #include <cmath>
     7 #include <stack>
     8 #include <queue>
     9 #include <vector>
    10 #include <string>
    11 #include <cstdio>
    12 #include <cstdlib>
    13 #include <cstring>
    14 #include <iostream>
    15 #include <algorithm>
    16 #define LL long long
    17 #define Min(a, b) ((a) < (b) ? (a) : (b))
    18 #define Max(a, b) ((a) > (b) ? (a) : (b))
    19 #define sqr(x) ((x)*(x))
    20 using namespace std;
    21 const int M = 510000;
    22 const int N = 100000;
    23 
    24 int n;
    25 LL ans;
    26 int trie[M+5][26], val[M+5], pos;
    27 char s[M+5];
    28 struct tt {
    29   int to, next;
    30 }edge[N+5];
    31 int path[N+5], top;
    32 int size[N+5], vis[N+5], tot;
    33 struct ss {
    34   int size, id;
    35   bool operator < (const ss &b) const{
    36     return size > b.size;
    37   }
    38 }tmp[N+5];
    39 stack<int>S;
    40 
    41 void add(int u, int v) {
    42   edge[++top].to = v;
    43   edge[top].next = path[u];
    44   path[u] = top;
    45 }
    46 void dfs(int u, int last) {
    47   ++tot;
    48   if (val[u]) add(last, val[u]), last = val[u];
    49   for (int i = 0; i < 26; i++)
    50     if (trie[u][i])
    51       dfs(trie[u][i], last);
    52 }
    53 void insert(char *s, int len, int num) {
    54   int u = 0;
    55   for (int i = len-1; i >= 0; i--) {
    56     if (!trie[u][s[i]-'a']) trie[u][s[i]-'a'] = ++pos;
    57     u = trie[u][s[i]-'a'];
    58   }
    59   val[u] = num;
    60 }
    61 void get_size(int u) {
    62   size[u] = 1;
    63   for (int i = path[u]; i; i = edge[i].next) {
    64     get_size(edge[i].to);
    65     size[u] += size[edge[i].to];
    66   }
    67 }
    68 void get_ans(int u, int fa) {
    69   int pos = 0; vis[u] = ++tot; ans += vis[u]-vis[fa];
    70   for (int i = path[u]; i; i = edge[i].next) {
    71     tmp[++pos].size = size[edge[i].to];
    72     tmp[pos].id = edge[i].to;
    73   }
    74   sort(tmp+1, tmp+1+pos);
    75   for (int i = 1; i <= pos; i++) S.push(tmp[i].id);
    76   while(pos--) {
    77     int v = S.top(); S.pop();
    78     get_ans(v, u);
    79   }
    80 }
    81 void work() {
    82   scanf("%d", &n);
    83   for (int i = 1; i <= n ;i++) {
    84     scanf("%s", s);
    85     insert(s, strlen(s), i);
    86   }
    87   dfs(0, 0);
    88   get_size(0);
    89   get_ans(0, 0);
    90   printf("%lld
    ", ans);
    91 }
    92 int main() {
    93   work();
    94   return 0;
    95 }
     
  • 相关阅读:
    CAP 可用性理解
    如何真正理解设计模式
    jdk concurrent 中 AbstractQueuedSynchronizer uml 图.
    test 博客园功能 和 搜索 seo 能力
    异或运算进行两数交换
    基本排序之冒泡排序
    对数器的使用(用来生成随机数组来判断排序算法是否正确)
    全双工,半双工,单工的概念
    mac下彻底卸载mysql方法
    AngularJs学习第一课 Hello World
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7648048.html
Copyright © 2011-2022 走看看