1 条题解
-
0
自动搬运
来自洛谷,原作者为

hezlik
绒布球搬运于
2025-08-24 22:02:30,当前版本为作者最后更新于2020-10-02 19:13:59,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意:给定一张 个点 条边的无向图,判定这张无向图是否是一张树之镜像图。若一张图可以由两棵一模一样的树共用叶节点得到,则称之为树之镜像图。
注意除根外,所有叶节点即度数为 的点必须全部合并。
数据范围:
- 。
考虑将问题分成两步解决:
- 找到被合并的叶节点。
- 依靠叶节点对图进行判定。
我们先完成第一步。
考虑找到图上的某一个环,并去掉环上的所有边。那么剩下的图将会是这样:

我绝对不会告诉你我贴的官方题解图。我们发现现在图中的连通块最多只包含两个环上的点。如果有包含三个以上环点的连通块可以直接判掉。
再考虑一个包含两个环点的连通块。我们分别让这两个环点 作为两棵树的根,那么对于任意一个叶节点,其到 的距离必然相同。
那么我们只需要找到任意一个环并将其所有边删去,再找到删去后一个包含两个环点的连通块,并分别从这两个环点出发 bfs,即可完成第一步。
但是我们发现,图中不一定存在环,环上的边删去后也可能不存在包含两个环点的连通块。
对于图中不存在环的情况,则必然存在一个度为 的点。而根据题意,显然度为 的点只能有 或 个。如果是 个点的话,则我们可以直接从这两个点出发进行 bfs。
而对于存在环但删去环边不存在一个连通块包含两个环点的情况,如果图中不存在两个度为 的点,则图只能是一个单独的环。而图是一个环的情况下,我们直接按照点数的奇偶性判定即可。
再来完成第二步。
首先我们需要确保叶子两边都是树。虽然看起来有很多东西要判,但其实我们可以直接判断点数与边数的关系,应满足这样一个关系式:
其中 为叶子的个数。
之后我们考虑从叶子往两个方向分别 bfs。容易发现此时叶子之间的对应关系已经被确定,所以上面的非叶节点之间的对应关系也必然确定,直接判断即可。
时间复杂度 。
代码如下:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100000; int n,m; struct side{ int y,next,tag; }e[N*2+9]; int lin[N+9],cs; void Ins(int x,int y){e[++cs].y=y;e[cs].next=lin[x];lin[x]=cs;} void Ins2(int x,int y){Ins(x,y);Ins(y,x);} int deg[N+9]; void into(){ scanf("%d%d",&n,&m); cs=1; for (int i=1;i<=m;++i){ int x,y; scanf("%d%d",&x,&y); Ins2(x,y); ++deg[x];++deg[y]; } } int ans; bool Check_ans_deg(){ //判断图中度数为 1 的点的数量是否合法 int cnt=0; for (int i=1;i<=n;++i) cnt+=deg[i]==1; return !cnt||cnt==2; } int vis[N+9]; void Dfs_vis(int k){ vis[k]=1; for (int i=lin[k];i;i=e[i].next) if (!vis[e[i].y]) Dfs_vis(e[i].y); } bool Check_ans_connect(){ //判断图是否连通,应该没必要 int cnt=0; for (int i=1;i<=n;++i) if (!vis[i]) ++cnt,Dfs_vis(i); for (int i=1;i<=n;++i) vis[i]=0; return cnt==1; } bool Check_ans_circle(){ //判断图是一个单独的环的情况 for (int i=1;i<=n;++i) if (deg[i]^2) return 1; ans=n&1^1; return 0; } int cir[N+9],cc; int sta[N+9],cst; bool Dfs_cir(int k,int fa){ //找到任意一个环 int res=0; vis[k]=1; sta[++cst]=k; for (int i=lin[k];i;i=e[i].next){ int y=e[i].y; if (y==fa) continue; if (vis[y]){ for (int i=cst;i>=1;--i){ cir[++cc]=sta[i]; if (sta[i]==y) break; } res=1; }else if (Dfs_cir(y,k)) res=1; if (res) break; } --cst; return res; } int bel[N+9]; void Dfs_bel(int k,int b){ bel[k]=b; for (int i=lin[k];i;i=e[i].next) if (!e[i].tag&&!bel[e[i].y]) Dfs_bel(e[i].y,b); } int rot[2]; void Get_rot(){ //找到可以作为根的两个点,放入 rot[0/1] 中 //如果有两个度数为 1 的点,直接用它们作为根 int cr=0; for (int i=1;i<=n;++i) if (deg[i]==1) rot[cr++]=i; if (cr) return; //否则考虑找到一个环,然后断掉所有环边,再找包含两个环点的连通块 Dfs_cir(1,0); for (int i=1;i<=cc;++i){ int x=cir[i],y=cir[i%cc+1]; for (int i=lin[x];i;i=e[i].next) if (e[i].y==y) e[i].tag=e[i^1].tag=1; } for (int i=1;i<=cc;++i) if (!bel[cir[i]]) Dfs_bel(cir[i],cir[i]); for (int i=1;i<=cc;++i) if (cir[i]^bel[cir[i]]) rot[0]=cir[i],rot[1]=bel[cir[i]]; } int dis[2][N+9]; queue<int>q; void Bfs_dis(int id,int st){ dis[id][st]=1;q.push(st); for (;!q.empty();){ int t=q.front();q.pop(); for (int i=lin[t];i;i=e[i].next) if (!dis[id][e[i].y]) dis[id][e[i].y]=dis[id][t]+1,q.push(e[i].y); } } int leaf[N+9],tag[N+9]; void Get_leaf(){ //找叶子 Bfs_dis(0,rot[0]); Bfs_dis(1,rot[1]); for (int i=1;i<=n;++i) if (!(leaf[i]=dis[0][i]==dis[1][i])) tag[i]=dis[0][i]>dis[1][i]; //将点分为三类 //leaf[i]=1 表示 i 是叶子 //tag[i] 表示 i 不是叶子的情况下,i 属于哪棵树 } int Dfs_leaf_cnt(int k){ int res=1; for (int i=lin[k];i;i=e[i].next) if (leaf[e[i].y]&&!vis[e[i].y]) res+=Dfs_leaf_cnt(e[i].y); return res; } bool Check_leaf_cnt(){ //根据点数与边数的关系判断图被叶子分开后,是不是两棵树 int cnt=0; for (int i=1;i<=n;++i) cnt+=leaf[i]; return n-2+cnt==m; } int ord[2][N+9],bfs[2][N+9],co[2]; int fa[2][N+9]; void Bfs_ord(){ for (int i=1;i<=n;++i) if (leaf[i]) q.push(i); for (;!q.empty();){ int t=q.front(),tt=tag[t];q.pop(); if (leaf[t]) ord[0][bfs[0][t]=++co[0]]=t,ord[1][bfs[1][t]=++co[1]]=t; else ord[tt][bfs[tt][t]=++co[tt]]=t; for (int i=lin[t];i;i=e[i].next){ int y=e[i].y; --deg[y]; if (bfs[tag[y]][y]) continue; fa[tag[y]][t]=y; if (deg[y]<=1) q.push(y); } } } bool Check_ans(){ //最后的判断 //先跑出一个bfs序,同时给两棵树上的点重新标号,再利用树上的父子关系判定 Bfs_ord(); for (int i=1;i<=co[0];++i) if (bfs[0][fa[0][ord[0][i]]]^bfs[1][fa[1][ord[1][i]]]) return 0; return 1; } void Get_ans(){ if (!Check_ans_deg()) return; if (!Check_ans_connect()) return; if (!Check_ans_circle()) return; Get_rot(); Get_leaf(); if (!Check_leaf_cnt()) return; ans=Check_ans(); } void work(){ Get_ans(); } void outo(){ puts(ans?"YES":"NO"); } int main(){ into(); work(); outo(); return 0; }
- 1
信息
- ID
- 3655
- 时间
- 1000ms
- 内存
- 250MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者