1. 程式人生 > 其它 >CF1499G Graph Coloring

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);
	}
}