「BalticOI 2011 Day2」Tree Mirroring 题解
本文网址:https://www.cnblogs.com/zsc985246/p/17539182.html ,转载请注明出处。
题目大意
现在有一棵树 ,复制一个完全相同的 ,并将这两棵树的叶子节点全部对应合并在一起,形成一个图,我们称这种图为对称图。
给定一个图,判断它是否为对称图。
思路
我们称原树中的叶子节点组成对称轴。
思考如果知道两棵树的根节点,能否找到对称轴上的所有点,进而判断是否为对称图。
发现一个点到两个根节点的距离相等当且仅当它在对称轴上。
那么我们可以对两个根节点各做一次 bfs 计算距离来求出这些点。
在 bfs 时记录每个点在两个根节点的 bfs 树上的父亲,然后我们就可以从对称轴向两边的父亲拓展,每次将两个点匹配,如果发现重复匹配则不合法。
这样我们就可以通过两个根节点来进行 判断合法性。
直接枚举根节点可以做到 的复杂度,可以拿到 分。
判定过程无法优化,考虑优化枚举过程。
发现如果有解,只要是对称的两个点都可以是根节点。
又因为对称轴上节点的度数一定为 ,所以可以直接枚举这个度数为 的点,然后以相邻的两个点作为根节点判断。
这样就把复杂度优化到了 ,拿到 分。
如果想要继续优化,必须要有一种能保证找到一对对称点的 方法。
考虑图中的环。
首先把环长为 和没有环的情况判掉。
发现如果合法,那么任意一个环外点到环上一定有且仅有两条路径。
再进一步地,我们发现这两条路径的终点是一对对称点。
那么就可以做到 了。
需要注意的是,给定的是任意图,所以有可能两边是对称的图而不是树。
对于此,我们只需要记录对称轴上的点数 ,然后判断 是否与 相等。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
#define pb push_back
const ll N=1e6+10;
using namespace std;
ll n,m;
vector<ll>e[N];
ll in[N];
ll fa[2][N],dis[2][N];//记录两个根节点的bfs树上的父亲和距离
void chk(ll opt,ll s){//处理fa[opt]和dis[opt]
For(i,1,n)fa[opt][i]=dis[opt][i]=-1;
queue<ll>q;
q.push(s);
fa[opt][s]=dis[opt][s]=0;
while(q.size()){
ll x=q.front();
q.pop();
for(ll y:e[x]){
if(~fa[opt][y])continue;
q.push(y);
dis[opt][y]=dis[opt][x]+1;
fa[opt][y]=x;
}
}
}
ll match[N];//match[i]记录与i匹配的点
bool calc(ll x,ll y){//拓展匹配
while(x&&y&&!match[x]&&!match[y]){
match[x]=y,match[y]=x;//匹配
x=fa[0][x],y=fa[1][y];//跳父亲
}
return match[x]==y&&match[y]==x;//是否重复匹配
}
bool check(ll s1,ll s2){//判断图是否合法,s1和s2为两个根节点
chk(0,s1),chk(1,s2);
ll ans=1,cnt=0;
For(i,1,n){
if(dis[0][i]==dis[1][i]){//距离相等
ans&=calc(fa[0][i],fa[1][i]);//拓展匹配
++cnt;//对称轴上点的个数
}
}
return ans&&n+cnt-2==m;//判断对称的是树
}
ll top,s[N];//记录dfs经过的节点,方便找环
ll vis[N];//标记经过的点,特别地,环上的点vis为2
bool find(ll x,ll fat){//找环
s[++top]=x;
vis[x]=1;
for(ll y:e[x]){
if(y==fat)continue;
if(vis[y]){
do{vis[s[top]]=2;}while(s[top--]!=y);//标记环上的点
return true;
}
if(find(y,x))return true;//找到就退出
}
return false;
}
bool bfs(ll sx){//找到sx到环的路径终点,sx是一个环外点
vector<ll>t;//记录路径终点
queue<ll>q;
q.push(sx);
vis[sx]=1;
while(q.size()){
ll x=q.front();
q.pop();
for(ll y:e[x]){
if(vis[y]){
if(vis[y]==2)t.pb(y);
continue;
}
vis[y]=1;
q.push(y);
}
}
return t.size()==2&&check(t[0],t[1]);//合法时只有两条路径
}
int main(){
scanf("%lld%lld",&n,&m);
For(i,1,m){
ll x,y;
scanf("%lld%lld",&x,&y);
e[x].pb(y),e[y].pb(x);
++in[x],++in[y];
}
//有度数为1的点
vector<ll>t;
For(i,1,n)if(in[i]==1)t.pb(i);
if(t.size()==2){//将这两个点作为根节点判断
if(check(t[0],t[1]))printf("YES");
else printf("NO");
return 0;
}
if(t.size()){//合法时只可能有2个或0个度数为1的点
printf("NO");
return 0;
}
find(1,0);//找环
For(i,1,n)if(vis[i]!=2)vis[i]=0;//清空
For(i,1,n){
if(!vis[i]){
if(bfs(i))printf("YES");
else printf("NO");
return 0;
}
}
//环长为n
if(n&1)printf("NO");
else printf("YES");
return 0;
}
尾声
如果你发现了问题,你可以直接回复这篇题解
如果你有更好的想法,也可以直接回复!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现