一道非常神仙的题 %%%%%%%%%%%%
首先看到这样的设问,做题数量多一点的同学不难想到这个题。事实上对于此题而言,题面中那个“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(带权并查集+欧拉回路)的更多相关文章
-
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 ...
-
Codeforces Round #181 (Div. 2) B. Coach 带权并查集
B. Coach 题目连接: http://www.codeforces.com/contest/300/problem/A Description A programming coach has n ...
-
codeforces 687D Dividing Kingdom II 带权并查集(dsu)
题意:给你m条边,每条边有一个权值,每次询问只保留编号l到r的边,让你把这个图分成两部分 一个方案的耗费是当前符合条件的边的最大权值(符合条件的边指两段点都在一个部分),问你如何分,可以让耗费最小 分 ...
-
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 ...
-
Codeforces 1156D 带权并查集
题意:给你一颗树,树边的权值可能是0或1,问先走0边,再走1边,或者只走1边的路径有多少条? 思路:对于一个点,假设通过0边相连的点一共有x个(包括自己),通过1边相连的有y个(包括自己),那么对答案 ...
-
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 ...
-
D. The Door Problem 带权并查集
http://codeforces.com/contest/776/problem/D 注意到每扇门都有两个东西和它连接着,那么,如果第i扇门的状态是1,也就是已经打开了,那么连接它的两个按钮的状态应 ...
-
POJ 1703 Find them, Catch them(带权并查集)
传送门 Find them, Catch them Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 42463 Accep ...
-
[NOIP摸你赛]Hzwer的陨石(带权并查集)
题目描述: 经过不懈的努力,Hzwer召唤了很多陨石.已知Hzwer的地图上共有n个区域,且一开始的时候第i个陨石掉在了第i个区域.有电力喷射背包的ndsf很自豪,他认为搬陨石很容易,所以他将一些区域 ...
随机推荐
-
System.Web.Script.Serialization引用找不到的问题
之前在项目中要使用JavascriptSerializer这个类,需要引入System.Web.Script.Serialization命名空间,但是在添加引用中找不到这个命名空间,后来才得知Syst ...
-
利用SecureCRT上传、下载文件(使用sz与rz命令)
sz用法: 下载一个文件 sz filename 下载多个文件 sz filename1 filename2 下载dir目录下的所有文件,不包含dir下的文件夹 sz dir/* 下载文件存放位置在s ...
-
Windows phone 8 学习笔记(6) 多任务(转)
Windows phone 8 是一个单任务操作系统,任何时候都只有一个应用处于活跃状态,这里的多任务是指对后台任务的支持.本节我们先讲讲应用程序的运行状态,然后看看支持的后台任务,包括:后台代理.后 ...
-
UVa442 Matrix Chain Multiplication
// UVa442 Matrix Chain Multiplication // 题意:输入n个矩阵的维度和一些矩阵链乘表达式,输出乘法的次数.假定A和m*n的,B是n*p的,那么AB是m*p的,乘法 ...
-
OpenGL8-直接分配显存-极速绘制(Opengl1.5版本才有)
视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 /** * 这个例子介绍如何使用显卡内存进行绘制 下载地址 : ...
-
thinkphp表单上传文件并将文件路径保存到数据库中
上传单个文件,此文以上传图片为例,上传效果如图所示 创建数据库upload_img,用于保存上传路径 CREATE TABLE `seminar_upload_img` ( `id` int(11) ...
-
Android SQLite的使用1(非原创)
1.继承SQLiteOpenHelper :public class MyOpenHelper extends SQLiteOpenHelper {} 2.重写下面3个方法 package com.e ...
-
POJ 2774 后缀数组:查找最长公共子
思考:其实很easy.就在两个串在一起.通过一个特殊字符,中间分隔,然后找到后缀数组的最长的公共前缀.然后在两个不同的串,最长是最长的公共子串. 注意的是:用第一个字符串来推断是不是在同一个字符中,刚 ...
-
hibernate系列笔记(1)---Hibernate增删改查
Hibernate增删改查 1.首先我们要知道什么是Hibernate Hibernate是一个轻量级的ORMapping对象.主要用来实现Java和数据库表之间的映射,除此之外还提供数据查询和数据获 ...
-
JS——判断一个对象是否为空
判断一个对象是否为空对象,本文给出三种判断方法: 1.最常见的思路,for...in...遍历属性,为真则为"非空数组":否则为"空数组" 2.通过JSON自带 ...