题目
思路来源
官方题解
题解
针对每个连通块,单独考虑:
一方面,
任取连通块的某棵生成树,
对于任意非树边(u,v),把树边u到v上的所有边都选中,即被覆盖1次,
任取某个非树边集合S,会导致树边有些被覆盖奇数次,有些被覆盖偶数次,
仅保留覆盖奇数次的树边,连通块内的点的度数就均为偶数了
另一方面,
度数为偶数的点有欧拉回路,
可以取走一个环,使得剩下的边仍然满足存在欧拉回路的条件,
即欧拉回路可以被拆成若干个环,并与刚才选取非树边时所取的环的方式一一对应
所以,答案即为所有非树边任取非空子集,
即若非树边条数为x,答案为
由于涉及到类似图的连通块的动态维护,可以在线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;
}#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;
}