#P12103. [2024年联测]命运

[2024年联测]命运

https://www.luogu.com.cn/problem/P4672

#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;
}