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

链接🔗

「SCOI2016」 背单词

题解

处理后缀,我们可以将所有字符串串翻转,然后丢掉Trie中处理前缀
代价1是最不划算的,而且有办法完全避免,代价2 = 代价3
贪心策略,优先把前缀,优先把子树节点少的点放前面
将点抽出来单独建图,然后处理出每个点的儿子数量,然后在DFS求代价时,用排序优先算儿子数量小的点

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxd = 510000;
const int maxc = 26;
char str[maxd];
struct 
{
    int arr[maxd][maxc],tot;
    bool cnt[maxd];
    void init()
    {
        tot=0;
        memset(arr,-1,sizeof(arr));
        memset(cnt,0,sizeof(cnt));
    }
    void insert(char *str)
    {  
        int p = 0;int len = strlen(str);
        for(int i=len-1;i>=0;i--)
        {
            if(arr[p][str[i]-'a'] == -1)
                arr[p][str[i]-'a'] = ++tot;
            p = arr[p][str[i]-'a'];
        }
        cnt[p] = 1;
    }
}tree;
struct {
    int v,next;
}e[100100];
int eid,f[100100],size[100100],pos,sw[100100];
long long ans;
void init()
{
    memset(f,-1,sizeof(f));
    eid=0;
}
void addedge(int x)
{
    eid++;
    e[eid].v = eid;
    e[eid].next = f[x];
    f[x] = eid;
}
void build(int x,int y) // 父亲节点,字典树编号
{
    if(tree.cnt[y])
    {
        addedge(x);
        x = eid;
    }
    for(int i=0;i<26;i++)
        if(tree.arr[y][i]!=-1)
            build(x,tree.arr[y][i]);
}
void getsize(int x)
{
    size[x] = 1;
    for(int i=f[x];i+1;i=e[i].next)
        getsize(e[i].v),size[x]+=size[e[i].v];
}
bool cmp(int x,int y)
{
    return size[x] < size[y];
}
void dfs(int x,int y) // 当前节点编号,上一节点坐标
{
    pos++; 
    ans += pos-y;
    y = pos;
    int l = sw[0];
    for(int i=f[x];i+1;i=e[i].next)
        sw[++sw[0]] = e[i].v;
    int r = sw[0];
    sort(sw+l+1,sw+r+1,cmp);
    for(int i=l+1;i<=r;i++) dfs(sw[i],y);
    sw[0] = l;
}
int main()
{
    int n;
    // freopen("a.in","r",stdin);
    // freopen("k.out","w",stdout);
    init();
    tree.init();
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%s",str);
        tree.insert(str);
    }
     build(0,0);
     getsize(0);
     dfs(0,1);
     printf("%lld",ans);
    return 0;
}