tarjan
脑补一下会发现合法的边一定在所有的奇环上,却不在任何偶环上。
tarjan弄出一棵生成树,考虑每一条非树边,如果加上之后形成奇环则这个环上的边必选,否则必不选。对于不只含一条非树边构成的环,它一定能够被分成若干这些只含一条非树边构成的环,易证用小环来处理的效果能等价于大环。
一个树边合法,当且仅当它被包含在所有奇环,不包含于任何偶环。一个非树边合法,当且仅当只有一个奇环且它就在这个奇环上(画图或脑补)。
#include<cstdio>
#include<vector>
#include<queue>
#define N 200005
#define mkp(u,v) make_pair(u,v)
using namespace std;
namespace runzhe2000
{
int n, m, ans, last[N], ecnt = 1, dfn[N], timer, dep[N], fa[N], deg[N], tag[N];
struct edge{int next, to;}e[N<<1];
vector<pair<int,int> > ring[2];
queue<int> q;
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
void tarjan(int x, int from)
{
dfn[x] = ++timer;
for(int i = last[x]; i; i = e[i].next)
{
if((i^1) == from) continue;
int y = e[i].to;
if(!dfn[y]) fa[y] = x, dep[y] = dep[x] + 1, deg[x]++, tarjan(y, i);
else if(dfn[x] >= dfn[y])
{
int type = (dep[x] - dep[y] + 1) & 1;
ring[type].push_back(mkp(x, y));
}
}
}
void main()
{
scanf("%d%d",&n,&m);
for(int i = 1, a, b; i <= m; i++)
{
scanf("%d%d",&a,&b);
addedge(a, b);
addedge(b, a);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i, 0), tag[i] = -233;
for(int i = 0; i < (int)ring[1].size(); i++) tag[ring[1][i].first]++, tag[ring[1][i].second]--;
for(int i = 0; i < (int)ring[0].size(); i++) tag[ring[0][i].first]--, tag[ring[0][i].second]++;
for(int i = 1; i <= n; i++)
if(!deg[i]) q.push(i);
for(; !q.empty(); )
{
int x = q.front(); q.pop();
if(tag[x] == (int)ring[1].size()) ans++;
tag[fa[x]] += tag[x];
if(!(--deg[fa[x]])) q.push(fa[x]);
}
if(ring[1].size() == 1) ans++;
printf("%d\n",ans);
}
}
int main()
{
runzhe2000::main();
}