牛客练习赛108 E.琉焰(非树边性质/线段树分治+可撤销并查集 or LCT)

文章讨论了一种处理图的连通块的方法,涉及对生成树的选择和非树边的覆盖策略。通过维护欧拉回路和利用线段树或可撤销并查集,可以动态计算所有可能的非树边组合,从而得到连通块内点的偶数度数配置。代码示例展示了如何实现这一过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

思路来源

官方题解

题解

针对每个连通块,单独考虑:

一方面,

任取连通块的某棵生成树,

对于任意非树边(u,v),把树边u到v上的所有边都选中,即被覆盖1次,

任取某个非树边集合S,会导致树边有些被覆盖奇数次,有些被覆盖偶数次,

仅保留覆盖奇数次的树边,连通块内的点的度数就均为偶数了

另一方面,

度数为偶数的点有欧拉回路,

可以取走一个环,使得剩下的边仍然满足存在欧拉回路的条件,

即欧拉回路可以被拆成若干个环,并与刚才选取非树边时所取的环的方式一一对应

所以,答案即为所有非树边任取非空子集,

即若非树边条数为x,答案为2^{x}-1

由于涉及到类似图的连通块的动态维护,可以在线LCT做

也可以离线下来线段树分治+可撤销并查集做,

非树边即为成环边,即并查集合并时已经在同一个集合里的边,维护其条数tot即可

代码

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
using namespace std;
typedef long long ll;
const int maxn=2e5,N=maxn+10,mod=998244353;
typedef pair<int,int> P;
map<P,int> last;
vector<P>dat[4*N];
int n,m,cnt,par[N],sz[N];
P a;
int tot,ans[N],two[N];
int find(int x){
	return par[x]==x?x:find(par[x]);
}
bool merge(int x,int y,stack<P> &q){
	x=find(x),y=find(y);
	if(x==y)return 1;
	if(sz[x]<sz[y])swap(x,y);
	q.push(P(x,y));
	sz[x]+=sz[y];
	par[y]=x;
    return 0;
}
void undo(stack<P> &q){
	while(!q.empty()){
		int x=q.top().fi;
		int y=q.top().se;
		q.pop();
		sz[x]-=sz[y];
		par[y]=y;
	} 
} 
void update(int p,int l,int r,int ql,int qr,P v){
	if(ql<=l&&r<=qr){
		dat[p].pb(v);
		return;
	}
	int mid=(l+r)/2;
	if(ql<=mid)update(lson,ql,qr,v);
	if(qr>mid)update(rson,ql,qr,v);
}
void dfs(int p,int l,int r){
	stack<P>q;
    int cnt=0;
	for(auto v:dat[p])
	cnt+=merge(v.fi,v.se,q); // 非树边的数量的变更
	tot+=cnt;
    if(l==r)ans[l]=two[tot]-1;
	else{
		int mid=(l+r)/2;
		dfs(lson);
		dfs(rson);
	}
	undo(q);
    tot-=cnt;
}
int main(){
	scanf("%d%d",&n,&m);
    two[0]=1;
	for(int i=1;i<=m;++i){
        two[i]=two[i-1]*2%mod;
		scanf("%d%d",&a.fi,&a.se);
		if(last.count(a)){
			update(1,1,m,last[a],i-1,a);
			last.erase(a); 
		}
		else last[a]=i;
	}
	for(auto v:last) 
	update(1,1,m,v.se,m,v.fi);
	for(int i=1;i<=m;++i)
	par[i]=i,sz[i]=1;
	dfs(1,1,m);
	for(int i=1;i<=m;++i)
	printf("%d\n",ans[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值