jzoj4631. 【GDOI2017模拟7.15】背单词

这题题面毒性极强

题意大概是这样:

有一些字符串,你可以给它们排一下顺序,对于每一个字符串$s$,如果排在第$x$位,代价如下计算:

1.所有字符串中有$s$的后缀在$s$的后面,则代价为$n*n$

2.所有字符串中$s$的后缀都在$s$的前面,代价为$x-y$,$y$是$s$在所有字符串中位置最后的后缀的位置(很绕)

3.所有字符串中没有$s$的后缀,代价为$x$

发现第1类代价很高,可以忽略不记,第3类代价就是第二类代价的特殊情况

于是我们可以将所有关键点提出来建树,将没有后缀的点连向一个虚拟的位置为0的点(代表空串),这样子可以很好处理第3类代价

然后问题就转化成给一棵大小为n+1的树上的所有节点标0~n的号,要求每个节点标号互不相同,而且子节点的标号要大于父节点的

并且希望所有子节点标号-所有父节点标号值最小

显然,使一个子树的标号连续是最优的(我也不会证明)

然后可以发现先走子树较小的点标号会使答案最优(证明类似排队接水问题,留坑)

#include<bits/stdc++.h>
using namespace std;
char c[500010];
int ch[500020][26],o,ed[500020],n,s[500010],dfn[500010],ct;
vector<int>v[500010];
long long r;
int cmp(int x,int y){
    return s[x]<s[y];
}
void ins(char *s){
    int x=0,l=strlen(s);
    for(int i=l-1;~i;i--){
        if(!ch[x][s[i]-'a'])
            ch[x][s[i]-'a']=++o;
        x=ch[x][s[i]-'a'];
    }
    ed[x]++;
}
void dfs1(int x,int fa){
    int g=fa;
    if(ed[x]){
        v[fa].push_back(x);
        g=x;
        s[x]=1;
    }
    for(int i=0;i<26;i++)
        if(ch[x][i]){
            dfs1(ch[x][i],g);
            s[x]+=s[ch[x][i]];
        }
}
void dfs2(int x){
    dfn[x]=++ct;
    sort(v[x].begin(),v[x].end(),cmp);
    for(int i=0;i<v[x].size();i++){
        r+=ct+1-dfn[x];
        dfs2(v[x][i]);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",c);
        ins(c);
    }
    dfs1(0,0);
    dfs2(0);
    printf("%lld",r); 
}

 

转载于:https://www.cnblogs.com/rilisoft/p/11094132.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值