题解:P10777 BZOJ3706 反色刷

P10777 BZOJ3706 反色刷

前置芝士

连通块

静态维护连通块,并查集是不错的选择。但我发现题解区都是清一色的并查集,维护还麻烦,看不下去了。

返璞归真,直接 dfs 不就行了吗?

欧拉回路

友情链接:P7771 【模板】欧拉路径

根据《NOI 考纲 2025 修订版》,欧拉回路,偶图(二分图)为【6】级考点。

定义:

  • 如果图 \(G\) 中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
  • 如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
  • 具有欧拉回路的图称为欧拉图(简称 E 图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。

偶图(二分图)本质是欧拉图。

性质+判定(充要条件):

  • 一个无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数,且该图是连通图。
  • 一个有向图存在欧拉回路,当且仅当该图所有顶点的入度等于出度,且该图是连通图。
  • 一个无向连通图存在欧拉路径,当且仅当该图奇度顶点数为 0 或 2,且该图是连通图。
  • 一个有向图存在欧拉路径,当且仅当该图一个顶点出度比入度多一,另一个顶点入度比出度多一,其余顶点的入度等于出度,且该图是连通图。
  • 反之全部成立。

分析

黑边白边本质是什么?经过所有黑边奇数次,白边偶数次(包括不经过),就能满足条件。实际上,如果能经过所有黑边奇数次,就一定能经过所有黑边一次

Proof:假设一定经过一条黑边大于一次。

以三条为例:

--1-\                 /-1-
     o--o-3--     o--o--2-
--2-/                 \-3-

列出进入该边的点的位置,必有一边连接大于一个入点,那为什么不能这样呢:

---1                         /1---
   |   o--o-3--   o--o       \2---
---2                 \-3--


变成了只有一个入点,即只经过该边一次。

只经过每条黑边一次?还要回起点?不就是欧拉回路吗?

是不是只要判断只有黑边的图是否是欧拉图?是的。那答案是否是欧拉图的个数呢?不是。

我们可以还经过白边偶数次。如果几个黑边欧拉图经过白边连通,可以一次干完。只需按白边原路返回即可。

实现

问题变成了:

  1. 判断是否全都是欧拉图,即是否有解。
  2. 相互不连通的欧拉图个数。

第一步很简单,全都是欧拉图必须满足全都是偶点,即没有奇点,维护奇点个数即可。

第二步,动态维护连通块很麻烦,可以变成维护连通块内是否有欧拉图,输出有欧拉图的连通块个数。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,m,q;
int x[N],y[N],c[N];
int d[N];				//黑边点的度数 
int odd;				//奇点个数 
vector<int> g[N];		//不管黑边白边,只管连通 
int fa[N],tot;			//连通块编号 
int sz[N];				//连通块内度数总和,为 0 则全是白边 
void dfs(int u){
	fa[u]=tot;
	sz[tot]+=d[u];
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(fa[v])	continue;
		dfs(v);
	}
	return;
}
signed main(){
	cin>>n>>m;
	for(int i=0;i<m;i++){
		cin>>x[i]>>y[i]>>c[i];
		d[x[i]]+=c[i],d[y[i]]+=c[i];
		g[x[i]].push_back(y[i]);
		g[y[i]].push_back(x[i]);
	}
	for(int i=1;i<=n;i++)
		if(d[i]%2)
			odd++;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(fa[i])	continue;
		tot++;
		dfs(i);
		if(sz[tot])	ans++;
	}
	cin>>q;
	while(q--){
		int op,i;
		cin>>op;
		if(op==1){
			cin>>i;
			if(c[i]){
				c[i]=0;
				d[x[i]]--;
				d[y[i]]--;
				if(d[x[i]]%2)	odd++;
				else	odd--;
				if(d[y[i]]%2)	odd++;
				else	odd--;
				sz[fa[x[i]]]-=2;
				if(sz[fa[x[i]]]==0)	ans--;
			}
			else{
				c[i]=1;
				d[x[i]]++;
				d[y[i]]++;
				if(d[x[i]]%2)	odd++;
				else	odd--;
				if(d[y[i]]%2)	odd++;
				else	odd--;
				if(sz[fa[x[i]]]==0)	ans++;
				sz[fa[x[i]]]+=2;
			}
		}
		if(op==2){
			if(odd)	cout<<-1<<'\n';
			else	cout<<ans<<'\n';
		}
	}
#define _ 0
	return 0^_^0;
#undef _
}

完结撒花!!!

posted @ 2026-01-30 10:11  concert_b  阅读(0)  评论(0)    收藏  举报