CF1499G Graph Coloring

一、题目

点此看题

二、解法

首先考虑定边怎么做,考虑构造得到最小解,我们先把所有环删掉,然后原图就剩下的若干条路径,我们把度为奇数的点作为某一条路径的端点,度为偶数的点不作为端点,那么答案就取到了下界:\(\sum[deg[u]\%2=1]\)

题目要求动态加边,并且强制在线,那就真的只能加边了呗,我们讨论一个新加边的两个节点的情况:

  • 如果都不是路径的端点,那么可以直接把这条边当成路径加进去。
  • 如果其中一个是路径的端点,那么把这条边接到路径上面去。
  • 如果都是路径的端点,那么考虑把这两条路径连起来,考虑连在这两个点上的两条边如果颜色相同,那么直接连上去;如果颜色不同,那么翻转其中一条路径的所有边的颜色。

主要问题是维护翻转操作,我们把边看成点,考虑用带权并查集来维护。众所周知带权并查集是可以打标记的,并查集中我们用到根路径上的所有标记 \(rev\) 的异或和来表示这条边的颜色。

然后每个点维护 \(sum[0/1]\) 表示蓝边和红边的总和,翻转的时候可以不用交换它们而直接用 \(rev\) 作为下标。因为翻转只会翻转根,我们在翻转的时候维护一下哈希值即可,时间复杂度 \(O(n\alpha)\)

三、总结

最小化问题可以考虑构造出答案下界(\(\tt construction\ force\) 最喜欢这么考了,我总是想不到)

有整体标记和合并问题可以考虑带权并查集(这个权的含义实际上就是标记)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 400005;
const int MOD = 998244353;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,id,hs,fa[M],a[M],rev[M],to[M],sum[M][2];//0:bule 1:red
int find(int x)
{
	if(fa[x]==x) return x;
	if(fa[fa[x]]==fa[x]) return fa[x];
	int t=find(fa[x]);
	rev[x]^=rev[fa[x]];
	return fa[x]=t;
}
void tag(int x)
{
	x=find(x);
	hs=(hs-sum[x][rev[x]^1]+MOD)%MOD;
	rev[x]^=1;
	hs=(hs+sum[x][rev[x]^1])%MOD;
}
int col(int x)
{
	if(x==fa[x]) return rev[x];
	int t=find(x);
	return rev[x]^rev[t];
}
void merge(int x,int y)
{
	x=find(x);y=find(y);
	if(x==y) return ;
	sum[y][rev[y]]=(sum[y][rev[y]]+sum[x][rev[x]])%MOD;
	sum[y][rev[y]^1]=(sum[y][rev[y]^1]+sum[x][rev[x]^1])%MOD;
	fa[x]=y;rev[x]^=rev[y];
}
void link(int x,int y)
{
	id++;fa[id]=id;
	sum[id][0]=a[id]=(a[id-1]<<1)%MOD;
	//case1: a brand new path
	if(!to[x] && !to[y])
	{
		to[x]=to[y]=id;
		return ;
	}
	//case2: have a same vertex
	if(!to[x]) swap(x,y);
	if(!to[y])
	{
		if(!col(to[x])) tag(id);
		merge(to[x],id);
		to[x]=0;to[y]=id;
		return ;
	}
	//case3: have two vertex
	if(col(to[x])!=col(to[y])) tag(to[x]);
	if(!col(to[x])) tag(id);
	merge(to[x],id);
	merge(to[y],id);
	to[x]=to[y]=0;
}
signed main()
{
	n=read();m=read();q=read();
	a[0]=1;
	for(int i=1;i<=q;i++)
	{
		int u=read(),v=read();
		link(u,v+n);
	}
	q=read();
	while(q--)
	{
		int op=read();
		if(op==1)
		{
			int u=read(),v=read();
			link(u,v+n);
			printf("%d\n",hs);
		}
		else
		{
			int ans=0;
			for(int i=1;i<=id;i++)
				if(col(i)) ans++;
			printf("%d",ans);
			for(int i=1;i<=id;i++)
				if(col(i)) printf(" %d",i);
			puts("");
		}
		fflush(stdout);
	}
}
posted @ 2021-07-21 22:35  C202044zxy  阅读(56)  评论(0编辑  收藏  举报