一、题目
二、解法
首先考虑定边怎么做,考虑构造得到最小解,我们先把所有环删掉,然后原图就剩下的若干条路径,我们把度为奇数的点作为某一条路径的端点,度为偶数的点不作为端点,那么答案就取到了下界:(sum[deg[u]\%2=1])
题目要求动态加边,并且强制在线,那就真的只能加边了呗,我们讨论一个新加边的两个节点的情况:
- 如果都不是路径的端点,那么可以直接把这条边当成路径加进去。
- 如果其中一个是路径的端点,那么把这条边接到路径上面去。
- 如果都是路径的端点,那么考虑把这两条路径连起来,考虑连在这两个点上的两条边如果颜色相同,那么直接连上去;如果颜色不同,那么翻转其中一条路径的所有边的颜色。
主要问题是维护翻转操作,我们把边看成点,考虑用带权并查集来维护。众所周知带权并查集是可以打标记的,并查集中我们用到根路径上的所有标记 (rev) 的异或和来表示这条边的颜色。
然后每个点维护 (sum[0/1]) 表示蓝边和红边的总和,翻转的时候可以不用交换它们而直接用 (rev) 作为下标。因为翻转只会翻转根,我们在翻转的时候维护一下哈希值即可,时间复杂度 (O(nalpha))
三、总结
最小化问题可以考虑构造出答案下界(( t 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
",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);
}
}