bzoj4567 背单词
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
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 */