ZOJ4134 Unrooted Trie(树上DFS序列+前缀和)

干了一天终于把陕西省赛的I题补出来了。。。给自己鼓个掌

题意:

给出一棵树。

字典树的原理是从根节点开始,每个结点代表的字符串是它的父亲结点代表的字符串加上边上那个结点。

不允许出现两个结点代表的字符串相同。

询问有多少个结点可以作为根节点。

题解:

首先检查每个节点的边集,如果在边集中出现aaa或aabb这样的重复情况,必然是不行的,直接输出0

然后DFS,记录每个节点被搜到的时间和出栈的时间,那么这两个时间点之间的所有点就是该节点的子树。

然后遍历每个节点,首先确定这个节点边集中两条重复的边对应的点t1,t2。然后分类讨论。

如果这个节点在DFS序里在t1,t2的前面,则是t1,t2的根,那么合法的根节点就是t1,t2的子树,就确定了这个节点的合法根节点区间。

如果这个节点在DFS序里在t1,t2的中间,则合法的根节点是t1的子树的补集加上t2的子树,保存这个节点的合法根节点区间。

确定所有的节点的合法根节点区间之后,用前缀和扫描所有节点,最后符合所有限制区间的节点就是根节点。

PS:一定要用链式前向星存图。以后要养成链式前向星存图的习惯。用vector存图在面对极大数据量的时候会超时。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+14;
struct node {
    int from;
    int to;
    int next;
    int c;
}edge[maxn*3];
int head[maxn];
int tol;
int dfn[maxn];
int dfo[maxn];
int N;
int cnt;
int l1[maxn];
int l2[maxn];
int l3[maxn];
int r1[maxn];
int r2[maxn];
int r3[maxn];
int xz[maxn];
int visit[maxn];
int qzh[maxn];
int a[maxn]; 

void init () {
    memset(head,-1,sizeof(head));
    tol=0;
    cnt=0;
    memset(l1,0,sizeof(l1));
    memset(l3,0,sizeof(l3));
    memset(visit,0,sizeof(visit));
    memset(xz,0,sizeof(xz));
}
void addedge (int u,int v,int c) {
    edge[tol].from=u;
    edge[tol].to=v;
    edge[tol].c=c;
    edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].from=v;
    edge[tol].to=u;
    edge[tol].c=c;
    edge[tol].next=head[v];
    head[v]=tol++;
}

void dfs (int u) {
    visit[u]=1;
    dfn[u]=++cnt;
    for (int i=head[u];i!=-1;i=edge[i].next) {
        int v=edge[i].to;
        if (!visit[v]) dfs(v);
    }
    dfo[u]=cnt;
}

int check (int u) {
    int cnt[30]={0};
    for (int i=head[u];i!=-1;i=edge[i].next)
        cnt[edge[i].c]++;
    int sum=0;
    for (int i=0;i<26;i++) {
        if (cnt[i]>2) return 0;
        if (cnt[i]==2) sum++;
    }
    if (sum>1) return 0;
    return 1;
}

int isTrie () {
    for (int i=1;i<=N;i++) 
        if (!check(i)) return 0;
    return 1;
}

void getSeg (int u) {
    vector<int> a[30];
    //int ck[26]={0};
    for (int i=head[u];i!=-1;i=edge[i].next) {
        int v=edge[i].to;
        a[edge[i].c].push_back(v);
    }
    for (int i=0;i<26;i++) {
        if (a[i].size()<2) continue;
        int t1=a[i][0];
        int t2=a[i][1];
        if (dfn[t1]>dfn[t2]) swap(t1,t2);
        //printf("%d %d\n",t1,t2);
        if (dfn[u]>dfn[t1]) {
            l1[u]=1;
            r1[u]=dfn[u]-1;
            l2[u]=dfn[t2];
            r2[u]=dfo[t2];
            l3[u]=dfo[u]+1;
            r3[u]=N;
        }
        else {
            l1[u]=dfn[t1];
            r1[u]=dfo[t1];
            l2[u]=dfn[t2];
            r2[u]=dfo[t2];
        }
        break;
    }
    //printf("%d %d %d %d\n",l1[u],r1[u],l2[u],r2[u]);
}
int main () {
    int T;
    scanf("%d",&T);
    int u,v;
    char ch;
    while (T--) {
        scanf("%d",&N);
        init();
        for (int i=1;i<=N-1;i++) {
            scanf("%d %d %c",&u,&v,&ch);
            int c=ch-'a';
            addedge(u,v,c);
        } 
        
        if (!isTrie()) {
            printf("0\n");continue;
        }
        dfs(1);
        for (int i=1;i<=N;i++)
            getSeg(i);
        int root=0;
        int ck=0;
        memset(qzh,0,sizeof(qzh));
        memset(a,0,sizeof(a));
        for (int i=1;i<=N;i++) {
            if (!l1[i]) {
                ck++;continue;
            } 
            qzh[l1[i]]++;
            qzh[r1[i]+1]--;
            qzh[l2[i]]++;
            qzh[r2[i]+1]--;
            if (!l3[i]) continue;
            qzh[l3[i]]++;
            qzh[r3[i]+1]--;
        }
        int sum=0;
        for (int i=1;i<=N;i++) {
            sum+=qzh[i];
            a[i]+=sum;
            if (a[i]==N-ck) root++;
        }
        printf("%d\n",root);
    }
}

 

posted @ 2020-03-07 22:23  zlc0405  阅读(399)  评论(1编辑  收藏  举报