Codeforces 题面传送门 & 洛谷题面传送门

一道非常神仙的题 %%%%%%%%%%%%

首先看到这样的设问,做题数量多一点的同学不难想到这个题。事实上对于此题而言,题面中那个“Classical and Easy”的问题就是那题的弱化版,具体来说,借鉴那题的思路,我们考虑建立一个虚点 \(V\)​,然后对于所有度为奇数的点 \(x\),我们连一条 \(x\) 与 \(V\) 之间的双向边,然后跑欧拉回路。对于每一条原二分图中的边,假设其左部点为 \(x\),右部点为 \(y\),那么如果边 \((x,y)\) 在欧拉回路上的方向为 \(x\to y\) 那么我们给这条边染蓝,否则我们给这条边染红,不难发现这样每个点的 \(|r(x)-b(x)|\) 之和达到了理论下界,也就是 \(\sum\limits_{x}[deg_x\text{ is odd}]\)。

然后我就企图直接对着这个版本解决此题,想着怎么用 set 维护每一个环,怎么启发式合并,然后发现复杂度爆炸,心态也随之爆炸……

事实上,对于此题多组询问的版本而言,如果直接这么做那么会牵扯到虚点加边删边的问题,会导致问题变得异常麻烦。因此考虑怎样不建虚点来解决这个问题,显然对于原来包含虚点的图而言,如果我们在遍历包含虚点的连通块遍历到了一个包含虚点的环 \(V\to v_1\to v_2\to\cdots\to v_k\to V\)​,那么如果我们把 \(V\)​ 删掉,那就会得到一个欧拉路径,因此如果我们不建虚点,那么问题即可以转化为,要找到 \(C=\sum\limits_{x}[deg_x\text{ is odd}]\)​ 个欧拉路径与一些环,并将这些路径与环上的边定向。

考虑怎么维护这些环,不难发现我们加入一条边时,如果存在某两条路径分别以这条边的两个端点为端点,那么我们就要将这两条路径并起来。看到这个“并”,我们可以很自然地想到并查集维护每条路径中边的集合。不过注意到这里涉及到路径的定向问题,因此我们不能使用普通的并查集——我们需要带权并查集。具体来说,对于一条边我们记其为 \(0\) 表示这条边方向是由左部点连到右部点,\(1\) 则反过来。那么当我们加入一条边 \((x,y)\) 时:

  • 如果 \(x,y\) 都不是某条边的端点,那么我们就令新加入的这条边单独成一个集合,权值 \(0/1\) 皆可。
  • 如果 \(x,y\) 中恰好有一个是某条边的端点,不妨设 \(x\) 是某条边的端点,如果与 \(x\) 相连的路径上的边的权值为 \(1\),那么令新加入的这条边的权值为 \(0\),否则令新加入的边的权值为 \(1\),然后将两个集合并起来即可。
  • 如果 \(x,y\) 都是某条边的端点,这种 case 稍微有点麻烦。如果与 \(x,y\) 直接相连的两条边的权值相同,那么我们就令 \((x,y)\) 的权值为与 \(x\) 相连的边的权值异或一,然后将三个集合并起来。否则我们就将 \(y\) 所在路径中所有边的权值 flip 一下,然后还是令 \((x,y)\) 权值为与 \(x\) 相连的边的权值异或一,将三个集合并起来即可。

u1s1 在做这道题之前我甚至还不会带权并查集(主要是当时 2 years ago 没认真学,大概老师讲这东西的时候我在打游戏),主要思想大概就对于一个并查集,我们定义 \(x\) 点的权值为 \(w_x\),那么我们不维护 \(w_x\),instead 我们维护一个 \(w’_x\),满足 \(w_x\) 等于 \(x\) 到根节点路径上所有点的 \(w’\) 的和(或异或和、或 \(\min\),取决于你定义了啥运算),那么在路径压缩的时候,我们考虑先遍历其父亲 \(f\),那么在遍历完父亲之后,父亲的 \(w’\) 值就是父亲真正的的 \(w\) 值减去根节点的 \(w\) 值,那么我们就直接令 \(x\) 的父亲为根节点,然后令 \(x\) 的 \(w’\) 值为父亲此时的 \(w’\) 值加上 \(x\) 原来的 \(w’\) 值即可,这样查询一个点真正的 \(w\) 值就可以拿 \(x\) 在路径压缩后的 \(w’\) 与根节点的 \(w\) 相加,将一个连通块中所有点的权值加上(或异或上)某个数 \(x\) 就直接令该连通块根节点的权值加上(或异或上)\(x\) 即可,这个异常好懂(

时间复杂度 \(\mathcal O((n+q)\log n)\)。

const int MAXN=4e5;
const int MOD=998244353;
int n1,n2,m,mm,pw2[MAXN+5],tag[MAXN+5],f[MAXN+5],res=0,sum[MAXN+5][2];
int find(int x){
if(!f[x]) return x;if(!f[f[x]]) return f[x];
int fx=find(f[x]);tag[x]^=tag[f[x]];return f[x]=fx;
}
bool ask(int x){if(!f[x]) return tag[x];int fx=find(x);/*printf("%d %d\n",tag[fx],tag[x]);*/return tag[fx]^tag[x];}
void rev(int x){
// printf("reverse %d\n",x);
x=find(x);res=(res-sum[x][tag[x]]+MOD)%MOD;
tag[x]^=1;res=(res+sum[x][tag[x]])%MOD;
}
void merge(int x,int y){
x=find(x);y=find(y);if(x==y) return;f[x]=y;
// printf("merge %d %d\n",x,y);
res=(res-sum[x][tag[x]]+MOD)%MOD;res=(res-sum[y][tag[y]]+MOD)%MOD;
sum[y][tag[y]]=(sum[y][tag[y]]+sum[x][tag[x]])%MOD;
sum[y][tag[y]^1]=(sum[y][tag[y]^1]+sum[x][tag[x]^1])%MOD;
tag[x]^=tag[y];res=(res+sum[y][tag[y]])%MOD;
}
int con[MAXN+5];
void dealins(int x,int y){
int id=++mm;sum[id][0]=pw2[id];res=(res+pw2[id])%MOD;
if(!con[x]&&!con[y]) return con[x]=con[y]=id,void();
// printf("ins %d %d\n",x,y);
if(!con[x]) swap(x,y);
if(!con[y]){
// printf("%d %d\n",x,y);
if(!ask(con[x])) rev(id);//printf("%d\n",res);
merge(id,con[x]);con[x]=0;con[y]=id;
} else {
if(ask(con[x])!=ask(con[y])) rev(con[y]);
if(!ask(con[x])) rev(id);
merge(id,con[x]);merge(id,con[y]);con[x]=con[y]=0;
}
}
void prt_color(){
int cnt=0;
for(int i=1;i<=mm;i++) if(!ask(i)) cnt++;
printf("%d\n",cnt);
for(int i=1;i<=mm;i++) if(!ask(i)) printf("%d ",i);
printf("\n");
}
int main(){
scanf("%d%d%d",&n1,&n2,&m);
pw2[0]=1;for(int i=1;i<=MAXN;i++) pw2[i]=(pw2[i-1]<<1)%MOD;
for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),dealins(u,v+n1);
int qu;scanf("%d",&qu);
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){
int u,v;scanf("%d%d",&u,&v);
dealins(u,v+n1);printf("%d\n",res);
} else prt_color();
fflush(stdout);
}
return 0;
}
/*
2 2 2
1 1
2 2
2
1 1 2
2
*/

Codeforces 1499G - Graph Coloring(带权并查集+欧拉回路)的更多相关文章

  1. Codeforces Educational Codeforces Round 5 C. The Labyrinth 带权并查集

    C. The Labyrinth 题目连接: http://www.codeforces.com/contest/616/problem/C Description You are given a r ...

  2. Codeforces Round #181 (Div. 2) B. Coach 带权并查集

    B. Coach 题目连接: http://www.codeforces.com/contest/300/problem/A Description A programming coach has n ...

  3. codeforces 687D Dividing Kingdom II 带权并查集(dsu)

    题意:给你m条边,每条边有一个权值,每次询问只保留编号l到r的边,让你把这个图分成两部分 一个方案的耗费是当前符合条件的边的最大权值(符合条件的边指两段点都在一个部分),问你如何分,可以让耗费最小 分 ...

  4. CodeForces - 687D: Dividing Kingdom II (二分图&带权并查集)

    Long time ago, there was a great kingdom and it was being ruled by The Great Arya and Pari The Great ...

  5. Codeforces 1156D 带权并查集

    题意:给你一颗树,树边的权值可能是0或1,问先走0边,再走1边,或者只走1边的路径有多少条? 思路:对于一个点,假设通过0边相连的点一共有x个(包括自己),通过1边相连的有y个(包括自己),那么对答案 ...

  6. Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) C. Destroying Array 带权并查集

    C. Destroying Array 题目连接: http://codeforces.com/contest/722/problem/C Description You are given an a ...

  7. D. The Door Problem 带权并查集

    http://codeforces.com/contest/776/problem/D 注意到每扇门都有两个东西和它连接着,那么,如果第i扇门的状态是1,也就是已经打开了,那么连接它的两个按钮的状态应 ...

  8. POJ 1703 Find them, Catch them(带权并查集)

    传送门 Find them, Catch them Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 42463   Accep ...

  9. [NOIP摸你赛]Hzwer的陨石(带权并查集)

    题目描述: 经过不懈的努力,Hzwer召唤了很多陨石.已知Hzwer的地图上共有n个区域,且一开始的时候第i个陨石掉在了第i个区域.有电力喷射背包的ndsf很自豪,他认为搬陨石很容易,所以他将一些区域 ...

随机推荐

  1. 【UE4】异步加载关卡 LoadingScreen ( 蓝图和C++ )

    一般先跳转到一个临时的关卡,然后异步加载目标关卡,同时展示Loading界面 对于含有流关卡的目标关卡,可以先载入子关卡 蓝图异步加载 无进度条 C++ 异步加载关卡 LoadPackageAsync ...

  2. java中延时队列的使用

    最近遇到这么一个需求,程序中有一个功能需要发送短信,当满足某些条件后,如果上一步的短信还没有发送出去,那么应该取消这个短信的发送.在翻阅java的api后,发现java中有一个延时队列可以解决这个问题 ...

  3. qgis cookbook-QgsMapRendererJob学习

    学习到渲染(QgsMapRendererJob),按照教程所讲总是输出不了图像,看了一下qgis的测试源码,发现少了一句话,加上后就可以输出了! from qgis.core import * fro ...

  4. RF射频传输,原理介绍,三分钟看懂!发射功率、接收灵敏度详解!

    射频是什么? 官方说法:RF,Radio Frequency. (不懂的人,看了还是不懂,不过对于物联网行业的开发工程师.产品经理和项目经理,还是有需要对射频有个基础了解的.) 燚智能解读: 两个人, ...

  5. sort-list leetcode C++

    Sort a linked list in O(n log n) time using constant space complexity. C++ /** * Definition for sing ...

  6. 最短路计数(SPFA× Dijkstra√)

    题目描述 给出一个n个顶点m条边的无向无权图,顶点编号为1−n.问从顶点1开始,到其他每个点的最短路有几条. 输入格式 第一行包含2个正整数n,m,为图的顶点数与边数. 接下来M行,每行2个正整数x, ...

  7. poj 2311 Cutting Game (SG)

    题意: 有一张W*H的纸片. 每人每次可以横着撕或者竖着撕,先撕出1*1那一方胜. 数据范围: W and H (2 <= W, H <= 200) 思路: 很好抽象出游戏图的模型,用SG ...

  8. 应对gitee容量超限. 保留star/fork/评论

    应对gitee容量超限 进入企业版,"管理"-"仓库管理",点"清空仓库". 在E:\gitee目录上右击,"git bash h ...

  9. ADB WIFI无线调试真正摆脱usb数据线连接,一次也不用!

    常见的使用ADB无线调试步骤 手机"开发者模式"菜单中开启"USB调试" 和"无线调试",手机网络与电脑在同一网内; 手机使用USB与电脑进 ...

  10. Linux部署Apollo+.Net Core简单使用

    Apollo官方网站非常详细,以下只是本人学习过程的整理 一.概念 Apollo(阿波罗)是一款可靠的分布式配置管理中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并 ...