UOJ #138. 【UER #3】开学前的涂鸦

Description

红包是一个有艺术细胞的男孩子。
红包由于NOI惨挂心情不好,暑假作业又多,于是他开始在作业本上涂鸦。
一开始,他在纸上画了一棵 n 个节点的树。但是他觉得这样的画太简单了,体现不出他高超的绘画功底,于是他又额外画上了 k 条边。
然而他觉得这样画面太复杂,于是想删去一些边使得这个无向图仍然是连通的。
请帮红包求出删边的方案数。两个方案被认为是不同的当且仅当存在一条边在其中一组中被删而另一组中没有。(什么边都不删也算一种方案)

Solution

首先发现可能被删除的边一定是在环上的,如果我们构出一棵树来,那么剩下的非树边一定都是反祖边,并且反祖边的条数不超过 (10)
(Dzy Loves Chinese II) 的方法做,我们给所有非树边一个权值,然后把跨过的树边都异或上这个权值(树上差分实现就好了)
那么不连通的情况就是存在一个子集异或和为 (0).
但是边树太多了不好枚举,我们缩边:把权值相同的一条链缩为以条边,这条边权值 (w) 为与它相同相同的边的数目(删这条边有 (w) 种选择),缩点后的树的一种方案对应原图中 (Pi w_i) 中方案
缩完之后的边不会很大,具体数字和证明见 (UOJ) 题解,大约是一个 (bell)
然后在一边搜索的时候用一边用线性基判掉不合法的方案就行了

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+50,mod=998244353;
int head[N],nxt[N*2],to[N*2],num=1,n,m,dep[N],st[N],top=0,w[N],cnt=0;
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
inline void dfs(int x,int last){
	for(int i=head[x];i;i=nxt[i]){
		if(i==last)continue;
		int u=to[i];
		if(!dep[u]){
			dep[u]=dep[x]+1;dfs(u,i^1);w[x]^=w[u];
			if(w[u])st[++top]=w[u];
		}
		else if(dep[u]<dep[x]){
			w[x]^=1<<cnt;w[u]^=1<<cnt;
			st[++top]=(1<<cnt++);
		}
	}
}
int b[1005][15],ans=0;
struct data{int w,v;}e[N];
inline void bfs(int x,int t){
	if(x==cnt+1){
		ans=(ans+t)%mod;
		return ;
	}
	memcpy(b[x],b[x-1],sizeof(b[x]));
	bfs(x+1,t);
	for(int i=10,c=e[x].w;i>=0;i--){
		if(!(c>>i&1))continue;
		if(!b[x][i]){b[x][i]=c;break;}
		else c^=b[x][i];
		if(!c)return ;
	}
	bfs(x+1,1ll*t*e[x].v%mod);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  int x,y;
  scanf("%d%d",&n,&m);m=n+m-1;
  for(int i=1;i<=m;i++){
	  scanf("%d%d",&x,&y);
	  link(x,y);link(y,x);
  }
  dep[1]=1;dfs(1,1);
  sort(st+1,st+top+1);cnt=0;
  for(int i=1,t=1;i<=top;i++,t++)
	  if(i==top || st[i]!=st[i+1])e[++cnt]=(data){st[i],t},t=0;
  bfs(1,1);
  printf("%d
",ans);
  return 0;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8722847.html