[Trie][LuoguP3294][SCOI2016]背单词

Posted jacktangs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Trie][LuoguP3294][SCOI2016]背单词相关的知识,希望对你有一定的参考价值。

题意很迷,然后你可以发现一种情况就是为了告诉你这是贪心。

显然我们可以将单词倒序插入Trie树,然后按照子树内单词个数从小到大遍历,模拟统计答案。

然后我们就得到了一个优秀的40分代码。

#include <string.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long
#define ULL unsigned long long
#define Uint unsigned int 
using namespace std;

const int MAXN=510000+5;

int ch[MAXN][26],node,n,size[MAXN];
char s[MAXN];
bool ed[MAXN];

vector <int> nxt[MAXN];

inline void insert(char *x){
    int len=strlen(x+1),now=0;
    for(int i=len;i>=1;--i){
        int c=x[i]-'a';
        if(!ch[now][c]) ch[now][c]=++node;
        now=ch[now][c];
    }
    ed[now]=true;
}

inline void get_size(int now){
    size[now]=0;
    for(Uint i=0;i<nxt[now].size();++i){
        int v=nxt[now][i];
        get_size(v);
        size[now]+=size[v];
    }
    if(ed[now]) size[now]++;
}

LL ans=0;int tot=0;

bool cmp(const int &x,const int &y){
    return size[x]<size[y];
}

inline void get_ans(int now,int last){
    if(ed[now]) tot++,ans+=tot-last,last=tot;
    for(Uint i=0;i<nxt[now].size();++i){
        int v=nxt[now][i];
        get_ans(v,last);
    }
}

int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        scanf("%s",s+1);
        insert(s);
    }
    for(int i=0;i<=node;++i){
        for(int j=0;j<26;++j){
            if(ch[i][j]) nxt[i].push_back(ch[i][j]);
        }
    }
    get_size(0);
    for(int i=0;i<=node;++i) if(nxt[i].size())sort(nxt[i].begin(),nxt[i].end(),cmp);
    get_ans(0,0);
    cout<<ans;
    return 0;
}

为什么只有40分呢?

观察这样一组数据。(转自)

7
aa
aba
abb
ba
bb
bc
bd

我们可以得到这样一棵Trie树。

技术分享图片

然后我们会先走右边再走左边。这样就会导致先走bba,再走aa,显然不优。

我们可以通过删去不要的节点来避免这种情况。

技术分享图片

然后这就很优秀了。

事实上代码也就多一个rebuild函数。

#include <string.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long
#define ULL unsigned long long
#define Uint unsigned int 
using namespace std;

const int MAXN=510000+5;

int ch[MAXN][26],node,n,size[MAXN];
char s[MAXN];
bool ed[MAXN];

vector <int> nxt[MAXN];

inline void insert(char *x){
    int len=strlen(x+1),now=0;
    for(int i=len;i>=1;--i){
        int c=x[i]-'a';
        if(!ch[now][c]) ch[now][c]=++node;
        now=ch[now][c];
    }
    ed[now]=true;
}

int cnt;

inline void rebuild(int now,int last){
    if(ed[now]) nxt[last].push_back(++cnt),last=cnt;
    for(int i=0;i<26;++i){
        if(!ch[now][i]) continue;
        rebuild(ch[now][i],last);
    }
}

inline void get_size(int now){
    size[now]=1;
    for(Uint i=0;i<nxt[now].size();++i){
        int v=nxt[now][i];
        get_size(v);
        size[now]+=size[v];
    }
}

LL ans=0;int tot=0;

bool cmp(const int &x,const int &y){
    return size[x]<size[y];
}

inline void get_ans(int now,int last){
    if(now) tot++,ans+=tot-last,last=tot;
    for(Uint i=0;i<nxt[now].size();++i){
        int v=nxt[now][i];
        get_ans(v,last);
    }
}

int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        scanf("%s",s+1);
        insert(s);
    }
    rebuild(0,0);
    get_size(0);
    for(int i=0;i<=cnt;++i) if(nxt[i].size())sort(nxt[i].begin(),nxt[i].end(),cmp);
    get_ans(0,0);
    cout<<ans;
    return 0;
}

以上是关于[Trie][LuoguP3294][SCOI2016]背单词的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P3294 [SCOI2016]背单词

luoguP2657 [SCOI2009]windy数

bzoj3926/luoguP3346 [Zjoi2015]诸神眷顾的幻想乡(trie上构建广义后缀自动机)

P3294 [SCOI2016]背单词

[luogu] P3294 [SCOI2016]背单词 (贪心)

luoguP6623 [省选联考 2020 A 卷] 树(trie树)