• #2652. 背单词(word)


    题目描述

    Lweb 面对如山的英语单词,陷入了深深的沉思,「我怎么样才能快点学完,然后去玩三国杀呢?」。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 $n$ 个,现在我们从上往下完成计划表,对于一个序号为 $x$ 的单词(序号 $1 sim x-1$ 都已经被填入):

    - 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 $n imes n$ 颗泡椒才能学会;
    - 当它的所有后缀都被填入表内的情况下,如果在 $1 sim x-1$ 的位置上的单词都不是它的后缀,那么他吃 $x$ 颗泡椒就能记住它;
    - 当它的所有后缀都被填入表内的情况下,如果 $1 sim x-1$ 的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为 $y$ ,那么他只要吃 $x-y$ 颗泡椒就能把它记住。

    Lweb 是一个吃到辣辣的东西会暴走的奇怪小朋友,所以请你帮助 Lweb,寻找一种最优的填写单词方案,使得他记住这 $n$ 个单词的情况下,吃最少的泡椒。

    数据范围

    $1 le n le 100000$ , 所有字符的长度总和 $1 le |len| le 510000$

    题解

    好冗长难懂的题目qwq

    考虑反串建立 $trie$ ,将结束位置标为关键点,将关键点拉出构成一个树,可以看出要先取走父亲再取走儿子。

    问题就变为对每个点分配权值 $a$ 使得 $a_i in [1,n]$ , $a_i>a_{fa_i}$ 且 $sum a_i-a_{fa_i}$ 最小。

    可以发现 $sum a_i$ 使一定的,于是考虑 $sum a_{fa_i}$ 的贡献最大即可。

    考虑一下不同子树大小间的比较,即有两个子树 $x,y$ , $size_x<size_y$ ,如果先走 $x$ 子树贡献为 $A$ ,先走 $y$ 子树贡献为 $B$ ,所以先走 $x$ 子树两个子树的贡献是 $A+B+(size_x+1) imes size_y$ ,先走 $y$ 子树两个子树的贡献是 $A+B+(size_y+1) imes size_x$ ,作差结果为 $size_y-size_x>0$ ,所以先走小子树更优。

    因此从子树小的往下遍历即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=5.1e5+5,M=1e5+5;
    int n,t[N][26],c,s[N],z[N],v[N],e[M],a[N],hd[N],V[M],nx[M],in[N];
    char r[N]; long long ans;
    struct O{
        int x,y;
        friend bool operator < (const O& A,const O& B){
            return z[A.x]>z[B.x];
        }
    };
    priority_queue<O>q;
    void add(int u,int v){
        nx[++c]=hd[u];V[hd[u]=c]=v;
    }
    void dfs(int x,int lt){
        z[x]=s[x];
        if (s[x]) v[lt]++,add(lt,x),lt=x;
        for (int i=0;i<26;i++)
            if (t[x][i]) dfs(t[x][i],lt),z[x]+=z[t[x][i]];
    }
    int main(){
        cin>>n;
        for (int i=1,l,p;i<=n;i++){
            scanf("%s",r);
            l=strlen(r);p=0;
            for (int v,j=l-1;~j;j--){
                v=r[j]-97;
                if (!t[p][v])
                    t[p][v]=++c;
                p=t[p][v];
            }
            s[e[i]=p]=1;
        }
        c=0;dfs(0,0);
        for (int i=hd[0];i;i=nx[i])
            q.push((O){V[i],v[V[i]]});
        int i=0;
        while(!q.empty()){
            O x=q.top();q.pop();i++;
            ans+=1ll*(1-x.y)*i;
            for (int i=hd[x.x];i;i=nx[i])
                q.push((O){V[i],v[V[i]]});
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    experss路由工作原理
    mongoose学习参考
    express+nodejs 个人博客
    HTML5-CSS3学习20150410(一)
    js学习20150420(七)
    js学习20150401(六)
    js学习20150401(五)
    js学习20150401(四)
    【荐】使用eval()、new Function()将JSON字符串转换为JSON对象
    HTML5 Canvas核心技术—图形、动画与游戏开发.pdf7
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/11779028.html
Copyright © 2020-2023  润新知