生成树
显然,如果是一棵树的话,那么任意拿一条边都是没关系的。
因此,我们先在图上随便生成一棵树,由于这是无向图,因此非树边只有返祖边。
那么,我们考虑非树边形成的环的奇偶性(即环上点数量的奇偶性)。
1.对于奇环:
不难发现,奇环上我们必须选一条边。换言之,我们选的边必须在奇环上。
2.对于偶环:
也不难发现,偶环上是不能选边的。
那么,本题就有大致思路了:
1.先构建一棵树
2.判断非树边构成的环的奇偶性,如果是奇环,利用树上差分把非树边两端在树上的路径上的边都+1;否则都-1,同时统计奇环数量cnt
3.遍历整棵树,判断一条边被加过的次数是否为cnt,若符合,则将ans++
注意:如果只有一个奇环,那么那条形成奇环的非树边也是可以选的,因此要将ans++
代码:
#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;
int n,m,tot,head[MAXN],ST[MAXN],ED[MAXN],deep[MAXN],pre[MAXN][20],lg[MAXN],cnt[MAXN],sum,ans;
bool vis[MAXN],edge[MAXN*2],mark[MAXN];
struct node {
int ed,id,last;
} G[MAXN*4];
void Add(int st,int ed,int id) {
tot++;
G[tot]=node {ed,id,head[st]};
head[st]=tot;
}
vector<int> Tr[MAXN];
void DFS(int x,int fa) {
deep[x]=deep[fa]+1;
pre[x][0]=fa;
for(int i=1;(1<<i)<=deep[x];i++)pre[x][i]=pre[pre[x][i-1]][i-1];
vis[x]=true;
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed;
if(t==fa)continue;
if(vis[t])continue;
Tr[x].push_back(t);
Tr[t].push_back(x);
edge[G[i].id]=true;
DFS(t,x);
}
}
int LCA(int x,int y){
if(deep[x]<deep[y])swap(x,y);
while(deep[x]>deep[y])x=pre[x][lg[deep[x]-deep[y]]-1];
if(x==y)return x;
for(int i=lg[deep[x]]-1;i>=0;i--){
if(pre[x][i]==pre[y][i])continue;
x=pre[x][i],y=pre[y][i];
}
return pre[x][0];
}
void solve(int x,int fa){
vis[x]=true;
for(int i=0;i<Tr[x].size();i++){
int t=Tr[x][i];
if(t==fa)continue;
solve(t,x);
cnt[x]+=cnt[t];
}
}
int main() {
for(int i=1;i<=MAXN-10;i++)lg[i]=lg[i-1]+((1<<lg[i-1])==i);
memset(head,-1,sizeof(head));
scanf("%d %d",&n,&m);
for(int i=1; i<=m; i++) {
int x,y;
scanf("%d %d",&x,&y);
Add(x,y,i);
Add(y,x,i);
}
tot=0;
for(int i=1;i<=n;i++){
if(vis[i])continue;
DFS(i,0);
}
for(int i=1;i<=n;i++){
for(int j=head[i];~j;j=G[j].last){
int t=G[j].ed,id=G[j].id;
if(edge[id])continue;
int st=i,ed=t;
if(deep[st]<deep[ed])swap(st,ed);
ST[++tot]=st,ED[tot]=ed,edge[id]=true;
}
}
for(int i=1;i<=tot;i++){
int st=ST[i],ed=ED[i],lca=LCA(st,ed);
if((deep[st]+deep[ed]-2*deep[lca]+1)%2==0)cnt[st]--,cnt[ed]--,cnt[lca]+=2;
else cnt[st]++,cnt[ed]++,cnt[lca]-=2,sum++;
}
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++){
if(vis[i])continue;
mark[i]=true;
solve(i,0);
}
for(int i=1;i<=n;i++){
if(mark[i])continue;
if(cnt[i]==sum)ans++;
}
if(sum==1)ans++;
printf("%d",ans);
return 0;
}