树的杂记

某次模拟赛的T2 强迫症

题目大意:判断一棵树是否对称。

思路:首先,一棵树的重心一定在对称轴上。一棵树是否对称有两种情况:有点在对称轴上和没有点在对称轴上。没有点在对称轴上的情况一定有两个重心(但有两个重心并不一定没有点在对称轴上)。有两个重心时,我们只需要算出两个重心对应子树的hash值,然后比较,如果相等就对称,否则就选其中一个重心(可以任选,并不影响结果)按一个重心的情况做一遍;有一个重心【可以有两棵单独的自己对称的树】(或上述再做一遍的情况【只能有一棵单独的自己对称的树】)时,我们计算出这个重心所有子树的hash值,放到map里,计算出hash值对应为奇数的hash值个数,如果超过了能单独自己对称树的个数就不对称,否则就判断这棵树能否自己对称【同样只能有一棵单独的自己对称的树】,递归下去。

         考试的时候,穷举了根,就会忽略没有点在对称轴上的情况,同时没有想到很好的hash,所以就导致了分数不高。

         这道题目的hash的选择要十分小心,很多基于子树的hash如果拆开后可能会出现bug。所以在这道题中应用了位运算(异或是没有分配律的,所以可以避免很多问题)。

#include<iostream>

#include<cstdio>

#include<cstring>

#include<map>

#include<cstdlib>

#include<ctime>

#define maxnode 10005

#define pp 233

using namespace std;

int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},tot=0,

    root1,root2,maxsiz,n,siz[maxnode]={0},ha[30]={0};

char color[maxnode]={0};

map <unsigned long long,int> cnt,dui;

void add(int u,int v)

{

    ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;

    ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;

}

void getroot(int u,int fa)

{

    int i,j,mm=0;

    siz[u]=1;

    for (i=point[u];i;i=next[i])

        if ((j=en[i])!=fa)

        {

            getroot(j,u);siz[u]+=siz[j];mm=max(mm,siz[j]);

        }

    mm=max(mm,n-siz[u]);

    if (mm<=maxsiz)

    {

        if (mm<maxsiz){maxsiz=mm;root1=u;root2=0;}

        else root2=u;

    }

}

unsigned long long gethash(int u,int fa)

{

    int i,j;

    unsigned long long ss=0;

    for (i=point[u];i;i=next[i])

        if ((j=en[i])!=fa)

            ss+=gethash(j,u);

    return (ss*pp)^(ha[color[u]-'A']);

}

bool judge(int r1,int r2,int fa,int cn)

{

    int i,j,x,y,cc=0,zhan[3]={0};

    unsigned long long ss,s1,s2;

    if (r2==0)

    {

        cnt.clear();dui.clear();

        for (i=point[r1];i;i=next[i])

        {

            if ((j=en[i])==fa) continue;

            ss=gethash(j,r1);

            if (!cnt.count(ss)) cnt[ss]=0;

            ++cnt[ss];dui[ss]=j;

        }

        map<unsigned long long,int>::iterator it;

        for (it=cnt.begin();it!=cnt.end();++it)

            if (it->second %2==1) 

            {

              ++cc;if (cc<=cn) zhan[cc]=dui[it->first];

            }

        if (cc>cn) return false;

        for (i=1;i<=cc;++i)

            if (!judge(zhan[i],0,r1,1)) return false;

        return true;

    }

    else

    {

        s1=gethash(r1,r2);s2=gethash(r2,r1);

        if (s1==s2) return true;

        return (judge(r1,0,0,2));

    }

}

int main()

{

    freopen("tree.in","r",stdin);

    freopen("tree.out","w",stdout);

    

    int t,i,j,u,v;

    srand(time(0));

    for (i=0;i<26;++i) ha[i]=rand();

    scanf("%d",&t);

    while(t)

    {

        scanf("%d",&n);tot=0;

        memset(point,0,sizeof(point));

        memset(next,0,sizeof(next));

        for (i=1;i<=n;++i)

            while(scanf("%c",&color[i])==1)

              if (color[i]>='A'&&color[i]<='Z') break;

        for (i=1;i<n;++i)

        {

            scanf("%d%d",&u,&v);add(u,v);

        }

        maxsiz=n;getroot(1,root1=root2=0);

        if (judge(root1,root2,0,2)) printf("SYMMETRIC\n");

        else printf("NOT SYMMETRIC\n");

        --t;

    }

    

    fclose(stdin);

    fclose(stdout);

}
View Code

 

你可能感兴趣的:(树)