逛动物园 (带权并查集好题)

动物园里有三种动物,大象、狗爷和老鼠。大象能战胜狗爷,狗爷能战胜老鼠,老鼠能战胜大象。
动物园里 n 有个笼子,编号从 1 到 n,每一个笼子里都关了一个小动物,因为 Smart视力不好看不清笼子里的动物是大象还是老鼠,所以他假装每个笼子里三种动物以相同的概率出现,即一共有 3n 种可能的初始状态。
动物园的表演开始了,在表演过程中发生了m 个事件:
1 u v,管理员把第 v 个笼子拆掉,并把笼子里的动物关到了第 u 个笼子里。两个动物在同一个笼子里会打架,胜者会活下来。如果种类相同则原来在 u 中的动物会活下来(主场优势)
2 u,表示 Smart 向 Sarah 提了个问题,有多少种初始状态使得原来在第 u 个笼子里的动物现在还活着。
Sarah 觉得这道问题太水了不屑于做,于是把它交给了你。

Input

第一行输入两个整数 n,m,表示笼子的个数和事件个数。
接下来 m 行,每行描述了一个事件。输入保证在所有第一类事件中,笼子 u,v 在对应的事件发生时都还没有被拆掉。

Output

对于每一个第二类事件,输出一行一个整数,表示满足条件的初始状态数。答案可能很大,请对 998244353 取模后输出。

Sample Input

3 5
2 1
1 2 1
2 1
1 2 3
2 1

Sample Output

27
9
6

这是一道带权并查集并查集好题,我们考虑把v笼子里的动物移动到u里,枚举所有情况可知有 \frac{2}{3} 的概率u里的动物获胜,有 \frac{1}{3} 的概率v里的动物获胜。那么和并查集有什么关系呢?我们维护一个每个动物存活的概率,合并的时候用并查集。每次合并u,v的时候u的概率乘 \frac{2}{3},v的概率乘 \frac{1}{3} ,找父节点时进行压缩路径和更新权值,由于最后要模一个质数,我们用费马小定理加快速幂。

代码:

#include<bits/stdc++.h>
using namespace std;
const long long MOD=998244353;
long long f[1000005],w[500005],p;
long long ksm(long long a,long long b){
	long long res=1;
	while(b>0){
		if(b&1){
			res=(res*a)%MOD;
		}
		a=a*a%MOD;
		b=b>>1;
    }
	return res%MOD;
}
long long find(long long x){
	if(x==f[x]){
		return x;
	}
	long long t=find(f[x]);
	w[x]=(w[x]*w[f[x]])%MOD;
	return f[x]=t;
}
int main(){
	long long n,m;
	cin>>n>>m;
	long long p=1,inv;
	for(long long i=1;i<=n;i++){
		p=(3ll*p)%MOD;
	}
	inv=ksm(3,MOD-2)%MOD;
	for(long long i=1;i<=2*n;i++){
		f[i]=i,w[i]=1;
	}
	for(long long i=1;i<=m;i++){
		long long op;
		scanf("%lld",&op);
		if(op==1){
			long long x,y;
			scanf("%lld%lld",&x,&y);
			long long fx=find(x),fy=find(y);
			w[fx]=2*inv;
			w[fy]=inv;
			f[fx]=f[fy]=++n;
		}
		else{
			long long x;
			scanf("%lld",&x);
			find(x);
			printf("%lld\n",(1ll*p*w[x])%MOD);
		}
	} 
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值