• [bzoj4567][Scoi2016]背单词-Trie+贪心+模型转化


    Brief Description

    • 给你N个互不相同的字符串,记(S_i)为第i个字符串,现在要求你指定N个串的出现顺序,我们用(V_i)表示第i个字符串是第几个出现的,则V为1到N的一个排列。我们希望你指定的出现顺序可以使总代价最小
    • 一个出现顺序的代价的计算方法如下依次考虑第i个串(S_i)对代价的贡献:
      • 假如存在一个串(S_j), (S_j)(S_i)的后缀:
        • 假如存在一个串(S_j), (S_j)(S_i)的后缀,且(V_j>V_i)则第i个串对代价的贡献为(N imes N)
        • 否则,记(P_i)为所有满足(S_j)(S_i)的后缀的j中(V_j)的最大值,第i个串对代价的贡献为(V_i-P_i)
      • 否则,如果不存在一个串(S_j),使得(S_j)(S_i)的后缀,则第i个串对代价的贡献为(V_i)
      • 你需要输出这个最小的总代价.

    Algorithm Design

    首先可以证明第一个条件是没有用处的.

    然后我们视后缀为一种偏序关系, 那么字符串就构成了一颗树, 那么问题就转化成了给树上每个节点标号使得他的标号减去他的父亲的标号的和最小.

    这是一个经典的贪心问题. 我们选择每次都选择最小的size子树进行标号, 不难证明这样做的correctness.

    Code

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #define ll long long
    #define pa std::pair<int, int>
    const int maxn = 100000 + 100;
    const int maxlen = 520000 + 100;
    int n;
    struct edge {
      int to, next;
    } e[maxn << 1];
    char str[maxlen];
    int rt = 1, sz = 1, ch[maxlen][26], len, id[maxlen], tot, head[maxn],
        size[maxn], f[maxn], cnt, fa[maxn];
    std::vector<pa> v[maxn << 1];
    inline void ins(int id) {
      int p = rt;
      for (int i = len; i >= 1; i--) {
        int x = str[i] - 'a';
        if (!ch[p][x]) {
          ch[p][x] = ++sz;
        }
        p = ch[p][x];
      }
      ::id[p] = id;
    }
    inline void add_edge(int u, int v) {
      e[++tot].to = v;
      e[tot].next = head[u];
      fa[v] = u;
      head[u] = tot;
    }
    void dfs(int x, int fa) {
      if (id[x]) {
        add_edge(fa, id[x]);
        fa = id[x];
      }
      for (int i = 0; i < 26; i++) {
        if (ch[x][i])
          dfs(ch[x][i], fa);
      }
    }
    void dfs2(int x) {
      size[x] = 1;
      for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].to;
        dfs2(y);
        v[x].push_back(std::make_pair(size[y], y));
        size[x] += size[y];
      }
      std::sort(v[x].begin(), v[x].end());
    }
    void getf(int x) {
      if (x)
        f[x] = ++cnt;
      for (int i = 0; i < v[x].size(); i++)
        getf(v[x][i].second);
    }
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("input", "r", stdin);
    #endif
      scanf("%d", &n);
      for (int i = 1; i <= n; i++) {
        scanf("%s", str + 1);
        len = strlen(str + 1);
        ins(i);
      }
      dfs(1, 0);
      dfs2(0);
      getf(0);
      ll ans = 0;
      for (int i = 1; i <= n; i++)
        ans += f[i] - f[fa[i]];
      printf("%lld", ans);
    }
    
    
  • 相关阅读:
    window dos 设置网络
    linux学习资料
    KASS分布式文件系统(Kass File System)
    MySQL主从关系设置(转)
    BitNami一键安装Redmine(转)
    利用光纤磁盘阵列实现存储共享(转)
    下一代Jquery模板-----JsRender
    手机开发类型jquery的框架Zepto(API)
    Android开发之onClick事件的三种写法(转)
    HTTP协议详解(转)
  • 原文地址:https://www.cnblogs.com/gengchen/p/6607613.html
Copyright © 2020-2023  润新知