【重庆市NOIP模拟赛】仔细的检查
时限: 1 Sec 内存: 128 MB Special Judge
题目描述
nodgd家里种了一棵树,有一天nodgd比较无聊,就把这棵树画在了一张纸上。另一天nodgd更无聊,就又画了一张。
这时nodgd发现,两次画的顺序是不一样的,这就导致了原本的某一个节点u0在第一幅图中编号为u1,在第二副图中编号为u2。
于是,nodgd决定检查一下他画出的两棵树到底是不是一样的。nodgd已经给每棵树的节点都从1到n进行了编号,即每棵树有n个节点。
如果存在一个1到n的排列p1,p2…pn,对于第一幅图中的任意一条边(i,j),在第二幅图中都能找到一条边(pi,pj),则认为这两幅图中的树是一样的。
输入格式
第一行一个整数n,表示节点的总数。
接下来n−1行,每行两个整数,表示第一幅图中的每一条边。
接下来n−1行,每行两个整数,表示第二幅图中的每一条边。
输出格式
如果两幅图的树是一样的,第一行输出”YES”,接下来1行输出一个1到n的排列p1,p2,……,pn,两个数之间用空格间隔。当多个排列都满足题意时,你可以随便输出一个。
如果两幅图的树是不一样的,只输出一行”NO”。
注意输出的时候不要加引号。
输入样例
3 1 2 2 3 1 3 3 2
输出样例
YES 1 3 2
提示
【样例解释1】
肉眼可见,1-2-3和1-3-2显然是一样的两棵树。不过这可能不是唯一的符合题意的排列。
数据范围:n<=100000
题解
树哈希
方法有很多
下面介绍普通的方法
设h[u]为以u为根子树的哈希值,g[u]为以u为根时整棵树的哈希值,bas[x]为一个随机权值数组
则有h[u]=1+h[son]*bas[siz[son]]
g[son]=(g[u]-h[son]*bas[siz[son]])*bas[n-siz[son]]+h[son]
于是我们就可以把以每个点为根的全树哈希值作为判定一个的标志
把A树中的哈希值放入map,映射到一个stack里面,每次查询一下B树中的某个节点哈希值
如果map中存在对应的哈希值,我们就可以从这个stack中取出一个元素来作为答案
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
using namespace std;
int n;
#define N 100005
const int mod1=1000000009;
const int mod2=998244353;
int fir[2][N],to[2][2*N],nxt[2][2*N],cnt[2];
void adde(int a,int b,int flg)
{
to[flg][++cnt[flg]]=b;nxt[flg][cnt[flg]]=fir[flg][a];fir[flg][a]=cnt[flg];
to[flg][++cnt[flg]]=a;nxt[flg][cnt[flg]]=fir[flg][b];fir[flg][b]=cnt[flg];
}
int h[2][N][2],g[2][N][2],siz[2][N],fa[2][N],bas[N][2];
void dfs1(int u,int flg)
{
siz[flg][u]=1;h[flg][u][0]=h[flg][u][1]=1;
int v,p;
for(p=fir[flg][u];p;p=nxt[flg][p]){
v=to[flg][p];
if(v!=fa[flg][u]){
fa[flg][v]=u;
dfs1(v,flg);
siz[flg][u]+=siz[flg][v];
h[flg][u][0]=1ll*(1ll*h[flg][u][0]+1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1)%mod1;
h[flg][u][1]=1ll*(1ll*h[flg][u][1]+1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2)%mod2;
}
}
}
void dfs2(int u,int flg)
{
if(u==1){
g[flg][u][0]=h[flg][u][0];
g[flg][u][1]=h[flg][u][1];
}
int v,p;
for(p=fir[flg][u];p;p=nxt[flg][p]){
v=to[flg][p];
if(v!=fa[flg][u]){
g[flg][v][0]=1ll*((1ll*g[flg][u][0]-1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1+1ll*mod1)%mod1*bas[n-siz[flg][v]][0]%mod1+1ll*h[flg][v][0])%mod1;
g[flg][v][1]=1ll*((1ll*g[flg][u][1]-1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2+1ll*mod2)%mod2*bas[n-siz[flg][v]][1]%mod2+1ll*h[flg][v][1])%mod2;
dfs2(v,flg);
}
}
}
map<pair<int,int>,stack<int> > mp;
int ans[N];
int main()
{
//freopen("data21.in","r",stdin);
//freopen("mydata21.out","w",stdout);
int i,u,v,p=100007;
bas[0][0]=bas[0][1]=1;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v,0);
bas[i][0]=1ll*bas[i-1][0]*p%mod1;
bas[i][1]=1ll*bas[i-1][1]*p%mod2;
}
dfs1(1,0);dfs2(1,0);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v,1);
}
dfs1(1,1);dfs2(1,1);
for(i=1;i<=n;i++)
mp[make_pair(g[0][i][0],g[0][i][1])].push(i);
for(i=1;i<=n;i++){
if(mp.count(make_pair(g[1][i][0],g[1][i][1]))){
ans[mp[make_pair(g[1][i][0],g[1][i][1])].top()]=i;
mp[make_pair(g[1][i][0],g[1][i][1])].pop();
}
else{
printf("NO\n");
return 0;
}
}
printf("YES\n%d",ans[1]);
for(i=2;i<=n;i++)
printf(" %d",ans[i]);
printf("\n");
}
但是这个做法是错的
它虽然对于每个点都找到了哈希值相等的对应点,但是它并没有保证最后答案的树满足A树中边的了解情况
如这组样例
4
2 1
2 3
3 4
3 2
3 1
1 4
正确答案应该是
YES
2 3 1 4
而上述程序会输出
YES
4 3 1 2
所以,我们还是要一个dfs来计算答案
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
using namespace std;
int n;
#define N 100005
const int mod1=1000000009;
const int mod2=998244353;
int fir[2][N],to[2][2*N],nxt[2][2*N],cnt[2];
void adde(int a,int b,int flg)
{
to[flg][++cnt[flg]]=b;nxt[flg][cnt[flg]]=fir[flg][a];fir[flg][a]=cnt[flg];
to[flg][++cnt[flg]]=a;nxt[flg][cnt[flg]]=fir[flg][b];fir[flg][b]=cnt[flg];
}
int h[2][N][2],g[2][N][2],siz[2][N],fa[2][N],bas[N][2];
void dfs1(int u,int flg)
{
siz[flg][u]=1;h[flg][u][0]=h[flg][u][1]=1;
int v,p;
for(p=fir[flg][u];p;p=nxt[flg][p]){
v=to[flg][p];
if(v!=fa[flg][u]){
fa[flg][v]=u;
dfs1(v,flg);
siz[flg][u]+=siz[flg][v];
h[flg][u][0]=1ll*(1ll*h[flg][u][0]+1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1)%mod1;
h[flg][u][1]=1ll*(1ll*h[flg][u][1]+1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2)%mod2;
}
}
}
void dfs2(int u,int flg)
{
if(u==1){
g[flg][u][0]=h[flg][u][0];
g[flg][u][1]=h[flg][u][1];
}
int v,p;
for(p=fir[flg][u];p;p=nxt[flg][p]){
v=to[flg][p];
if(v!=fa[flg][u]){
g[flg][v][0]=1ll*((1ll*g[flg][u][0]-1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1+1ll*mod1)%mod1*bas[n-siz[flg][v]][0]%mod1+1ll*h[flg][v][0])%mod1;
g[flg][v][1]=1ll*((1ll*g[flg][u][1]-1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2+1ll*mod2)%mod2*bas[n-siz[flg][v]][1]%mod2+1ll*h[flg][v][1])%mod2;
dfs2(v,flg);
}
}
}
map<pair<int,int>,stack<int> > mp;
int ans[N],vis[2][N];
void dfs(int u0,int u1)
{
ans[u0]=u1;
vis[0][u0]=1;
vis[1][u1]=1;
int v0,p0,v1,p1;
for(p0=fir[0][u0];p0;p0=nxt[0][p0]){
v0=to[0][p0];
if(vis[0][v0]) continue;
for(p1=fir[1][u1];p1;p1=nxt[1][p1]){
v1=to[1][p1];
if(vis[1][v1]) continue;
if(g[0][v0][0]==g[1][v1][0]&&g[0][v0][1]==g[1][v1][1]){
dfs(v0,v1);
break;
}
}
}
}
int main()
{
//freopen("data21.in","r",stdin);
//freopen("_mydata21.out","w",stdout);
int i,u,v,p=100007;
bas[0][0]=bas[0][1]=1;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v,0);
bas[i][0]=1ll*bas[i-1][0]*p%mod1;
bas[i][1]=1ll*bas[i-1][1]*p%mod2;
}
dfs1(1,0);dfs2(1,0);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v,1);
}
dfs1(1,1);dfs2(1,1);
for(i=1;i<=n;i++)
mp[make_pair(g[0][i][0],g[0][i][1])].push(i);
int A,B=1;
for(i=1;i<=n;i++){
if(mp[make_pair(g[1][i][0],g[1][i][1])].empty()){
printf("NO\n");
return 0;
}
if(i==1)
A=mp[make_pair(g[1][i][0],g[1][i][1])].top();
mp[make_pair(g[1][i][0],g[1][i][1])].pop();
}
dfs(A,B);
printf("YES\n%d",ans[1]);
for(i=2;i<=n;i++)
printf(" %d",ans[i]);
printf("\n");
}