很容易发现与奇环和偶环有关,但是仔细想清楚和怎么用程序实现还是很难的

但是我们dfs一边后,会发现能形成环的边都变成了返祖边

而且这些返祖边对于答案大部分情况下都没有贡献

偶环上的边都不能做出贡献,当且仅当一条边被所有奇环包含时才能做出贡献

有两个性质如下:

性质1:如果有超过两个奇环,则所有非树边都没有贡献,否则有1的贡献

性质2:两条特殊边组合也不会对答案产生任何贡献

情况1:                                情况2:                              情况3:

 

枚举后就可以证明以上的规律

那么每条返祖边都是修改通往根节点的一条链,就可以用查分非常简单的实现

代码如下:(昨天被STL迷住了,所以写了又慢又丑的迭代器STL代码)

#include<bits/stdc++.h>
using namespace std;
vector <pair<int,int> > edge[400005];
int vis[400005],n,m,one[400005],two[400005],ans,depth[400005];
int shu1,shu2,sum1,sum2,pd[400005],father[400005];
void dfs1(int x)
{
	pd[x]=1;
	for(vector <pair<int,int> > ::iterator it=edge[x].begin(),ed=edge[x].end();it!=ed;it++)
	{
		int y=it->second;
		if(vis[it->first]) continue;
		vis[it->first]=vis[(it->first)^1]=1;
		if(pd[y])
		{
			if((depth[x]&1)==(depth[y]&1))
			{
				one[x]++;
				one[y]--;
				sum1++;
			}
			else 
			{
				two[x]++;
				two[y]--;
				sum2++;
			}
		}
		else 
		{
			father[y]=it->first;
			depth[y]=depth[x]+1;
			dfs1(y);
		}
	}
}
void dfs2(int x)
{
	for(vector <pair<int,int> > ::iterator it=edge[x].begin(),ed=edge[x].end();it!=ed;it++)
	{
		int y=it->second;
		if(father[y]==it->first)
		{
			dfs2(y);
			one[x]+=one[y];
			two[x]+=two[y];
		}
	}
}
int main()
{
	freopen("voltage.in","r",stdin);
	freopen("voltage.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&shu1,&shu2);
		edge[shu1].push_back(make_pair(i*2,shu2));
		edge[shu2].push_back(make_pair(i*2+1,shu1));
	}
	depth[1]=1;
	dfs1(1);
	dfs2(1);
	for(int i=1;i<=n;i++)
	{
		if(father[i]&&one[i]==sum1&&two[i]==0) ans++;
	}
	if(sum1==1) ans++;
	cout<<ans;
	return 0;
}