[SCOI2016]背单词
题目描述
Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:
—————序号 单词—————
1 2......n-2n-1 n—————
然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入):
- 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 n*n 颗泡椒才能学会;
- 当它的所有后缀都被填入表内的情况下,如果在 1...x-1 的位置上的单词都不是它的后缀,那么你吃 x 颗泡椒就能记住它;
- 当它的所有后缀都被填入表内的情况下,如果 1...x-1的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为 y ,那么你只要吃 x-y 颗泡椒就能把它记住。
Lweb 是一个吃到辣辣的东西会暴走的奇怪小朋友,所以请你帮助 Lweb ,寻找一种最优的填写单词方案,使得他记住这 n 个单词的情况下,吃最少的泡椒。
输入格式
输入一个整数 n ,表示 Lweb 要学习的单词数。
接下来 n 行,每行有一个单词(由小写字母构成,且保证任意单词两两互不相同)1<=n<=100000, 所有字符的长度总和 1<=|len|<=510000
输出格式
Lweb 吃的最少泡椒数
首先贪心一波
规律1显然不划算,划去1
然后,根据规律2,3
对于若干子节点(后缀),不妨假设他们的子节点(后缀)到他们 x-y (规律3)已经最优
那么将size小的放在size大的前显然要有优秀一些(自行模拟)
首先将后缀逆转成前缀
得到,每个具体点(单词结尾)的树
//ling ren zhi xi #include<bits/stdc++.h> #define re return #define dec(i,l,r) for(int i=l;i>=r;--i) #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=510005; char ss[maxn]; int n,m,tot=1,cnt; long long ans; int tr[maxn][26],fa[maxn],id[maxn],pos[maxn],size[maxn]; vector<int>t[maxn]; inline void build(int x) { int len=strlen(ss); int now=1; dec(i,len-1,0) { if(!tr[now][ss[i]-'a']) tr[now][ss[i]-'a']=++tot; now=tr[now][ss[i]-'a']; } id[now]=x; } inline int find(int x) { re x==fa[x]?x:fa[x]=find(fa[x]); } inline void make(int x)//并查集,重构树 { int v; inc(i,0,25) if(v=tr[x][i]) { if(!id[v])fa[v]=find(x); else t[id[find(x)]].push_back(id[v]);
//find【x】是一个节点,在最初不是某个固定的结尾,可能为0 make(v); } } inline bool cmp(const int& a,const int &b)//活久见,vetor体内自定义排序 { re size[a]<size[b]; } inline void Get_son(int x)//树形遍历,找出子树大小排序 { size[x]=1; for(vector<int>::iterator it=t[x].begin();it!=t[x].end();++it) { int v=*it; Get_son(v); size[x]+=size[v]; } sort(t[x].begin(),t[x].end(),cmp); } inline void dfs(int x) { pos[x]=cnt++; for(vector<int>::iterator it=t[x].begin();it!=t[x].end();it++) { int v=*it; ans+=cnt-pos[x]; dfs(v); } } int main() { rd(n); inc(i,1,n) { scanf("%s",ss); build(i); } inc(i,1,tot)fa[i]=i; make(1);//重新规划树 Get_son(0);//得到每个点的size dfs(0);//Get_ans printf("%lld",ans); }