zoukankan      html  css  js  c++  java
  • BZOJ4567:[SCOI2016]背单词——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4567

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

    出题的语死早啊……

    给一个洛谷题解的翻译版题面:https://www.luogu.org/blog/skylee/solution-p3294

    给你n个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串s的位置为x):

    1.排在s后面的字符串有s的后缀,则代价为n^2;

    2.排在s前面的字符串有s的后缀,且没有排在s后面的s的后缀,则代价为x-y(y为最后一个与s不相等的后缀的位置);

    3.s没有后缀,则代价为x。

    求最小代价和。

    首先1代价我们是完全可以避免的,且能证明如果产生了1操作一定可以把这个操作优化下去从而得到更优的解。

    所以直接考虑每个串后缀之间的关系,将串反建trie,然后根据前缀确立一棵树,则问题转换成将每个点的id附1~n的权值,问所有节点父亲id-儿子id的和的最小值。

    不是很显然的,我们可以发现我们标完一整个子树,比我们几棵子树一起标要优。

    于是我们按照子数大小从小到大枚举,然后转移即可。

    #include<map>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef double dl;
    const int N=1e5+5;
    const int S=510010;
    struct tree{
        int ed,son[26];
    }tr[S];
    struct node{
        int to,nxt;
    }e[N];
    int n,head[N],sz[N],cnt,tot=1;
    char s[S];
    inline void add(int u,int v){
        e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
    }
    void insert(int id){
        int l=strlen(s),now=1;
        for(int i=l-1;i>=0;i--){
        int v=s[i]-'a';
        if(!tr[now].son[v])tr[now].son[v]=++tot;
        now=tr[now].son[v];
        }
        tr[now].ed=id;
    }
    void dfs1(int u,int fa){
        if(tr[u].ed)add(fa,tr[u].ed);
        for(int i=0;i<26;i++){
        int v=tr[u].son[i];
        if(v)dfs1(v,tr[u].ed?tr[u].ed:fa);
        }
    }
    struct son{
        int sz,v;
    }tmp[N];
    ll f[N];
    inline bool cmp(son a,son b){return a.sz<b.sz;}
    void dfs2(int u){
        sz[u]=1;
        for(int i=head[u];i;i=e[i].nxt)dfs2(e[i].to),sz[u]+=sz[e[i].to];
        int m=0;
        for(int i=head[u];i;i=e[i].nxt)tmp[++m]=(son){sz[e[i].to],e[i].to};
        sort(tmp+1,tmp+m+1,cmp);
        int idx=1;
        for(int i=1;i<=m;i++){
        f[u]+=f[tmp[i].v]+idx;idx+=tmp[i].sz;
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
        scanf("%s",s);insert(i);
        }
        dfs1(1,0);dfs2(0);
        printf("%lld
    ",f[0]);
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9168112.html
Copyright © 2011-2022 走看看