[SCOI2016]背单词

题目描述

Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:

—————序号 单词—————

1 2......n-2n-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 个单词的情况下,吃最少的泡椒。

输入格式

输入一个整数 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); }

 

 

 

posted @ 2019-08-20 20:34  凉如水  阅读(262)  评论(0编辑  收藏  举报