题解: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--
变成了只有一个入点,即只经过该边一次。
只经过每条黑边一次?还要回起点?不就是欧拉回路吗?
是不是只要判断只有黑边的图是否是欧拉图?是的。那答案是否是欧拉图的个数呢?不是。
我们可以还经过白边偶数次。如果几个黑边欧拉图经过白边连通,可以一次干完。只需按白边原路返回即可。
实现
问题变成了:
- 判断是否全都是欧拉图,即是否有解。
- 相互不连通的欧拉图个数。
第一步很简单,全都是欧拉图必须满足全都是偶点,即没有奇点,维护奇点个数即可。
第二步,动态维护连通块很麻烦,可以变成维护连通块内是否有欧拉图,输出有欧拉图的连通块个数。
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 _
}
完结撒花!!!

浙公网安备 33010602011771号