[UOJ181]密码锁

这个完全图随机定向后是一个竞赛图,将它的强连通分量缩点后我们得到的东西类似一条链,每个点往它后面的所有点连边

在这条链上,我们可以把点划分为两个点集$S,T$使得没有从$T$中点到$S$中点的边($S,T$对应到原图中同样满足条件),那么原图强连通分量个数$=$缩点后的点数$=$划分方案数$+1$,所以期望下的划分方案数$+1$即为答案

令$p_{x,y}$表示$(x,y)$被定向成$x\rightarrow y$的概率(不算非特殊边),那么划分方案数的期望就是$\sum\limits_{S\cup T=V}\prod\limits_{x\in S,y\in T}p_{x,y}$,所以当$m=0$时答案为$\sum\limits_{i=1}^{n-1}\binom ni\left(\frac12\right)^{i(n-i)}$

当$m\neq0$时,我们先硬点每条边的概率为$\frac12$,对于题中给出的那些特殊边,若它的概率为$p$,那么它对概率的贡献为$2p$

对每一个特殊边连接的弱连通块,求出$f_i$表示有$i$个点$\in S$的概率,这里直接枚举弱连通块的每个点属于$S$还是$T$即可,因为有$m$条边的连通块最多有$m+1$个点,所以枚举的时间复杂度为$O(2^m)$

每求完一个弱连通块的$f$,将它和答案$g$卷积即可,最终答案即为$\sum\limits_{i=1}^{n-1}g_i\left(\frac12\right)^{i(n-i)}$

所以说要是做过这个题的话一眼秒掉GDOI2018D1T4应该不成问题...

#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353,i2=499122177,i5=595552581;
int mul(int a,int b){return(ll)a*b%mod;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
void inc(int&a,int b){(a+=b)%=mod;}
int d[40][40],p[40],a[40],b[40],f[40],g[40],h[40],E,M,n;
bool v[40],s[40];
void dfs(int x){
	v[x]=1;
	p[M++]=x;
	for(int i=1;i<=n;i++){
		if(~d[x][i]){
			if(x<i){
				a[E]=x;
				b[E]=i;
				E++;
			}
			if(!v[i])dfs(i);
		}
	}
}
int main(){
	int m,i,j,k,x,y,z,t,S;
	scanf("%d%d",&n,&m);
	memset(d,-1,sizeof(d));
	while(m--){
		scanf("%d%d%d",&x,&y,&z);
		d[x][y]=mul(z,i5);
		d[y][x]=mul(10000-z,i5);
	}
	f[0]=1;
	for(i=1;i<=n;i++){
		if(!v[i]){
			E=M=0;
			dfs(i);
			memset(g,0,sizeof(g));
			for(j=0;j<1<<M;j++){
				S=0;
				for(k=0;k<M;k++)S+=(s[p[k]]=j>>k&1);
				t=1;
				for(k=0;k<E;k++){
					if(s[a[k]]&&!s[b[k]])t=mul(t,d[a[k]][b[k]]);
					if(!s[a[k]]&&s[b[k]])t=mul(t,d[b[k]][a[k]]);
				}
				inc(g[S],t);
			}
			memset(h,0,sizeof(h));
			for(j=0;j<=n-M;j++){
				for(k=0;k<=M;k++)inc(h[j+k],mul(f[j],g[k]));
			}
			memcpy(f,h,sizeof(h));
		}
	}
	t=1;
	for(i=1;i<n;i++)inc(t,mul(f[i],pow(i2,i*(n-i))));
	inc(t,mod);
	printf("%d",mul(t,pow(10000,n*(n-1))));
}

转载于:https://www.cnblogs.com/jefflyy/p/9736556.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值