P3294 [SCOI2016]背单词

P3294 [SCOI2016]背单词

Trie+贪心

倒插进树+取出重建+子树处理+贪心遍历

倒插进树:把后缀转化为前缀,所以把字符串倒着插进Trie中

取出重建:重新建立一棵以单词为节点的树,如果存在包含(前缀)关系就连边

子树处理:处理出每棵树的大小用于贪心

贪心遍历:遍历整棵新树,累加答案

关于贪心:每次找到最小的子树统计答案

end.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
struct data{
    int nxt[27],end;
    data(){memset(nxt,0,sizeof(nxt)); end=0;}
}a[510003];
vector <int> g[100002]; //存边
int n,cnt,len,siz[100002];
long long f[100002];
char q[510003];
inline bool cmp(const int &A,const int &B) {return siz[A]<siz[B];}
inline void read_q(){
    char c=getchar(); len=0;
    while(c<'a'||c>'z') c=getchar();
    while('a'<=c&&c<='z') q[len++]=c,c=getchar();
}
inline void insert_(int id){ //建树
    read_q();
    int u=0;
    for(int i=len-1;i>=0;--i){
        int p=q[i]-'a';
        if(!a[u].nxt[p]) a[u].nxt[p]=++cnt;
        u=a[u].nxt[p];
    }a[u].end=id; //编号代替单词
}
inline void rebuild(int x,int p){ //重新建树
    if(a[x].end) g[p].push_back(a[x].end); //存在前缀关系连边
    for(int i=0;i<26;++i){
        int to=a[x].nxt[i];
        if(!to) continue;
        rebuild(to,a[x].end ? a[x].end:p);
    }
}
inline void dfs1(int x){
    siz[x]=1;
    for(int i=0;i<g[x].size();++i){
        int to=g[x][i];
        dfs1(to);
        siz[x]+=siz[to];
    }
    sort(g[x].begin(),g[x].end(),cmp); //按子树从小到大排序
}
inline void dfs2(int x){
    f[x]=x? 1:0;
    long long tmp=0;
    for(int i=0;i<g[x].size();++i){
        int to=g[x][i];
        dfs2(to);
        f[x]+=f[to]+tmp; //贪心累加答案
        tmp+=siz[to];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) insert_(i);
    rebuild(0,0);
    dfs1(0);
    dfs2(0);
    printf("%lld",f[0]);
    return 0;
}

 

posted @ 2018-09-09 18:25  kafuuchino  阅读(209)  评论(0编辑  收藏  举报