很容易发现与奇环和偶环有关,但是仔细想清楚和怎么用程序实现还是很难的
但是我们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;
}