某次模拟赛的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); }