题解 电压机制

传送门

求对一张无向图黑白染色后只有一条边两端点的颜色相同的方案数

这样的边一定在所有奇环的交集中,且不属于任何偶环

  • 无向图上找奇环/偶环、找奇环/偶环交集:考虑dfs树上的每个点有一个深度,可以利用一条非树边两端点的深度判断

又有性质:考虑两条非树边所构成的环,分类讨论后发现它们不可能在奇环的交集上
水题解实锤了
于是统计每条边被包含在多少个奇环和偶环中
可以对每条非树边的两个端点做树上差分实现
特判只有一条非树边的情况即可

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int head[N], size=1;
struct edge{int from, to, next; bool del;}e[N<<2];
inline void add(int s, int t) {e[++size].to=t; e[size].from=s; e[size].next=head[s]; e[size].del=0; head[s]=size;}

namespace force{
	int ans;
	int col[N];
	bool dfs(int u, int in_edge) {
		in_edge^=1;
		for (int i=head[u],v; ~i; i=e[i].next) if (!e[i].del && i!=in_edge) {
			v = e[i].to;
			if (col[v]==col[u]) return 1;
			if (~col[v]) continue;
			col[v]=col[u]^1;
			if (dfs(v, i)) return 1;
		}
		return 0;
	}
	bool check(int s, int t) {
		memset(col, -1, sizeof(col));
		col[s]=0; col[t]=0;
		if (dfs(s, 0)) return 0;
		if (dfs(t, 0)) return 0;
		for (int i=1; i<=n; ++i) if (col[i]==-1) {
			col[i]=0;
			if (dfs(i, 0)) return 0;
		}
		return 1;
	}
	void solve() {
		for (int i=2; i<=m*2; i+=2) {
			e[i].del=e[i^1].del=1;
			if (check(e[i].from, e[i].to)) ++ans;
			e[i].del=e[i^1].del=0;
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task1{
	int cnt;
	bool vis[N];
	int dfs(int u, int in_edge) {
		in_edge^=1;
		vis[u]=1;
		for (int i=head[u],v; ~i; i=e[i].next) if (i!=in_edge) {
			v = e[i].to;
			if (vis[v]) {++cnt; return v;}
			int tem=dfs(v, i);
			if (~tem) {
				++cnt;
				return tem==u?-1:tem;
			}
		}
		return -1;
	}
	void solve() {
		dfs(1, 0);
		// cout<<"cnt: "<<cnt<<endl;
		if (cnt&1) printf("%d\n", cnt);
		else printf("%d\n", m-cnt);
		exit(0);
	}
}

namespace task{
	int odd[N], even[N], sodd[N], seven[N], dep[N], ans, oddcnt, fa[27][N], lg[N];
	bool vis[N], intree[N<<2];
	void dfs1(int u) {
		vis[u]=1;
		for (int i=1; i<26; ++i)
			if (dep[u]>(1<<i)) fa[i][u]=fa[i-1][fa[i-1][u]];
			else break;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (vis[v]) continue;
			fa[0][v]=u; dep[v]=dep[u]+1;
			intree[i]=intree[i^1]=1; dfs1(v);
		}
	}
	void dfs2(int u, int fa) {
		sodd[u]=odd[u]; seven[u]=even[u];
		for (int i=head[u],v; ~i; i=e[i].next) if (intree[i]) {
			v = e[i].to;
			if (v==fa) continue;
			dfs2(v, u);
			sodd[u]+=sodd[v]; seven[u]+=seven[v];
		}
		// cout<<"su: "<<sodd[u]<<' '<<seven[u]<<endl;
		if (u!=1 && sodd[u]==oddcnt && !seven[u]) ++ans;
	}
	int lca(int a, int b) {
		if (dep[a]<dep[b]) swap(a, b);
		while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
		if (a==b) return a;
		for (int i=lg[dep[a]]-1; ~i; --i)
			if (fa[i][a]!=fa[i][b])
				a=fa[i][a], b=fa[i][b];
		return fa[0][a];
	}
	void solve() {
		dep[1]=1; dfs1(1);
		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=2; i<=m*2; i+=2) if (!intree[i]) {
			int s=e[i].from, t=e[i].to, g=lca(s, t);
			// cout<<"st: "<<s<<' '<<t<<endl;
			if (dep[s]<dep[t]) swap(s, t);
			if ((dep[e[i].from]+dep[e[i].to])&1) {
				++even[s];
				even[t]+=(g!=s&&g!=t)?1:-1;
			}
			else {
				++odd[s]; ++oddcnt;
				odd[t]+=(g!=s&&g!=t)?1:-1;
			}
		}
		// cout<<"oddcnt: "<<oddcnt<<endl;
		dfs2(1, 0);
		if (oddcnt==1) {
			for (int i=2; i<=m*2; i+=2) if (!intree[i]) {
				ans+=((dep[e[i].from]+dep[e[i].to])&1)?0:1;
			}
		}
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);

	memset(head, -1, sizeof(head));
	n=read(); m=read();
	for (int i=1,u,v; i<=m; ++i) {
		u=read(); v=read();
		add(u, v); add(v, u);
	}
	// if (n==m) task1::solve();
	// else force::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-09-28 06:38  Administrator-09  阅读(2)  评论(0编辑  收藏  举报