CCPC2020(绵阳) C. Code a Trie

C. Code a Trie

题意大概就是给n个字符串,然后问能完全包含这些串的结点数最少的字典树的结点个数是多少。
首先,value相同的字符串一定在某一个结点截止,而这个点一定在这些结点的公共路径上,所以,这个点一定在这些串的结束结点的LCA上到根的路径上。
其次,同一个value值的所有儿子结点的子树中包含这个权值的子结点一定只能含有当前的权值,如果含有多于一个的话,就一定不能构成符合条件的字典树,如果两个不同权值所得的LCA相同,同样也无解。
然后我们再求最少的结点个数,首先所有的LCA到根的路径一定能包含所有的答案中的点,然后我们考虑将LCA尽可能往根上面跳,首先,一个LCA不能跳到其他的LCA上,而且也不能跳到之前的LCA跳的路径上,其次,不能跳到子树中含有大于等于两个这样的LCA的结点,这样我们就能算出最少的结点个数了。

#include
#define clean(x) memset(x,0,sizeof(x))
#define fil(x,n) fill(x,x+1+n,0)
#define inf 2000000009
#define maxn 500005
#define int long long
using namespace std;

int read()
{
    int x=1,res=0;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        x=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        res=res*10+(c-'0');
        c=getchar();
    }
    return res*x;
}

int tot=1,ch[maxn][26];
int cnt=0,text_num;
char a[maxn];
vector<int>g[maxn];
map<int,int>mp[maxn],ss;
int f[maxn][25],dep[maxn],ok[maxn],ff[maxn];
void dfs(int x) {
    for(int i=0;i<=20;i++) f[x][i+1]=f[f[x][i]][i];
    for(int i=0;i<=25;i++) {
        int v=ch[x][i];
        if(v) {
            f[v][0]=x;
            dep[v]=dep[x]+1;
            dfs(v);
            ok[x]=ok[x]+ok[v];
        }
    }
}
 
int lca(int a,int b) {
    if(dep[a]<dep[b]) swap(a,b);
    for(int i=20;i>=0;i--) {
        if(dep[f[a][i]]>=dep[b]) a=f[a][i];
    }
    if(a==b) return a;
    for(int i=20;i>=0;i--) {
        if(f[a][i]!=f[b][i]) {
            a=f[a][i];
            b=f[b][i];
        }
    }
    return f[a][0];
}

void insert(int len,int val) {
    int u=1;
    for(int i=1;i<=len;i++) {
        int c=a[i]-'a';
        if(!ch[u][c]) ch[u][c]=++tot;
        u=ch[u][c];
        mp[u][val]=1;
    }
    g[val].push_back(u);
}

void clear() {
    for(int i=1;i<=tot;i++) mp[i].clear(),ok[i]=0,ff[i]=0;
    for(int i=1;i<=tot;i++) for(int j=0;j<=25;j++) ch[i][j]=0;
    for(int i=0;i<=cnt;i++) g[i].clear();
    ss.clear();
}

void solve()
{
    int n=read();tot=1;
    cnt=0;
    for(int i=1;i<=n;i++) {
        cin>>(a+1);int len=strlen(a+1);int val=read();
        if(ss[val]==0) ss[val]=++cnt;
        val=ss[val];
        insert(len,val);
    }
    dep[1]=1;dfs(1);
    for(int i=1;i<=cnt;i++) {
        int lc=-1;
        for(int k:g[i]) {if(lc==-1) lc=k;else lc=lca(lc,k);}
        if(ok[lc]) {printf("Case #%d: -1\n",++text_num);clear();return;}
        ok[lc]=1;ff[lc]=1;
        for(int j=0;j<=25;j++) {
            int c=ch[lc][j];
            if(c==0||mp[c][i]==0) continue;
            if(mp[c][i]==1&&mp[c].size()>1) {printf("Case #%d: -1\n",++text_num);clear();return;} 
        }
    }
    dfs(1);int ans=0;
    for(int i=1;i<=tot;i++) if(ok[i]) ans++;
    for(int i=1;i<=cnt;i++) {
        int lc=-1;
        for(int k:g[i]) {if(lc==-1) lc=k;else lc=lca(lc,k);}
        while(lc!=1&&ok[lc]==1&&ff[f[lc][0]]==0) {
            lc=f[lc][0];ans--;ff[lc]=1;
        }
    }
    printf("Case #%d: %d\n",++text_num,ans);
    clear();
}

signed main()
{
    int t=read();
    while(t--)
    solve();
    return 0;   
}

你可能感兴趣的:(ACM,#,区域赛补题,#,数据结构,c语言,深度优先,图论)