码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4567 背单词

时间:2018-10-06 14:26:02      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:code   main   gets   nbsp   namespace   ash   怎么   完成   后缀   

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树。当然我们需要反向存字符串。

然后我们可以知道,对于一个字符串,只有它的第一个点(翻转之前)是对我们有用的,比如:ABCD,BCD我们只用记录A有一条向B的边。

我们先正常建树,然后标记所有末尾点,如果这个点的父亲是它的前缀,那么就连一条父亲到他的边。

对于取字符策略,考虑贪心。对于一个点,我们优先选择那个最小的子树,从小到大,依次枚举。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define REP(i,k,n) for(long long i=k;i<=n;i++)
#define in(a) a=read()
#define MAXN 510010
using namespace std;
inline long long read(){
    long long x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch==-)
            f=-1;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-0;
    return x*f;
}
long long n;
long long cnt=1;
long long f[MAXN];
long long total=0,head[MAXN],nxt[MAXN],to[MAXN];
long long tree[MAXN][26],isend[MAXN];
long long size[MAXN];
long long dfn[MAXN],ind=0,ans=0;
long long top=0;
struct node{
    long long si,in;
}arr[MAXN];
inline void adl(long long a,long long b){
    total++;
//    cout<<total<<" "<<a<<" "<<b<<endl;
    to[total]=b;
    nxt[total]=head[a];
    head[a]=total;
    return ;
}
inline void insert(char *s){
    long long u=1;
    for(long long i=strlen(s)-1;i>=0;i--){
        if(!tree[u][s[i]-a])  tree[u][s[i]-a]=++cnt;
        u=tree[u][s[i]-a];
    }
    //cout<<u<<endl;
    isend[u]=1;
    return ;
}
inline void build(long long u,long long f){
    if(isend[u]){
        adl(f,u);
        f=u;
    }
    REP(i,0,25)
        if(tree[u][i])
            build(tree[u][i],f);
    return ;
}
inline void getsize(long long u){
    size[u]=1;
    for(long long e=head[u];e;e=nxt[e]){
        getsize(to[e]);
        size[u]+=size[to[e]];
    }
    return ;
}
inline bool cmp(node a,node b){
    return a.si<b.si;
}
inline void getans(long long u){
    dfn[u]=++ind;
    long long m=top+1;
    for(long long e=head[u];e;e=nxt[e]){
        arr[++top].in=to[e];
        arr[top].si=size[to[e]];
    }
    if(top<m)  return ;
    sort(arr+m,arr+top+1,cmp);
    REP(i,m,top){
        getans(arr[i].in);
        ans+=dfn[arr[i].in]-dfn[u];
        //cout<<u<<" "<<arr[i].in<<" "<<ans<<endl;
    }
    top=m-1;
    return ;    
}
int main(){
    in(n);
    char c[100010];
    REP(i,1,n){
        scanf("%s",c);
        insert(c);
    }
    build(1,1);
    getsize(1);
    getans(1);
    cout<<ans;
}
/*
4
da
dcba
ba
cba
*/

 

 

 

bzoj4567 背单词

标签:code   main   gets   nbsp   namespace   ash   怎么   完成   后缀   

原文地址:https://www.cnblogs.com/jason2003/p/9747191.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!