【小学期实训】附加题题解——Good Karma

[状压dp+容斥原理] 实训附加题——Good Karma

题目描述

题目链接

题目

「天空度假山庄」中有一个 nm 边的无向图,图中点的编号分别为 1,2,,n,边的编号分别为 1,2,,m

度假山庄已经很久没有打扫了,因此图中的边已经模糊不清。

具体来说:

  • i 条边连接的两个端点分别为 uivi
  • 这条边上落下了许多灰尘,因此云浅有 pi 的概率看不到这条边。这里保证 0pi1,piQ

在云浅看到的图中,若 1 号点所在的连通块大小为 x,则云浅会认为这张图的「美观程度」为x2

云浅想要求出这张图的「美观程度」的期望值。她还要帮 Oshiro 打扫山庄,因此她找到了你求助。

为了避免精度误差,本题的输入输出格式较为特殊,详细信息将在【输入格式】与【输出格式】中说明。

输入格式

第一行两个正整数 n,m

接下来 m 行,第 i+1 行有 4 个正整数 ui,vi,xi,yi,表示一条边。其中:

  • ui,vi 表示这条边的两个端点。
  • 云浅看不到这条边的概率即为 pi=xixi+yi

输出格式

一行一个正整数表示这张图的「美观程度」的期望值。

为了避免精度误差,你只需要求出答案对 998244353 取模后的值即可。

更具体地,设答案为 AB,其中 gcd(A,B)=1,你需要输出一个 C 使得 B×CA(mod 998244353)

可以证明这样的 C 一定存在且唯一。

数据范围

对于 100% 的数据,1n17,1mn(n1)2,1xi,yi<998244353,xi+yi998244353

测试点编号 n m
12 =8 12
34 =9 12
56 =10 15
78 =11 25
910 =12 30
1112 =13 40
1314 =14 n(n1)2
1516 =15 n(n1)2
1718 =16 n(n1)2
1920 =17 n(n1)2

格式说明

输出时每行末尾的多余空格,不影响答案正确性

输入、输出要求

要求使用「文件输入、输出」的方式解题,输入文件为 karma.in,输出文件为 karma.out

样例输入1

4 4
1 2 1 1
2 3 1 2
3 1 1 3
2 4 2 3

样例输出1

465847375

样例输入2

2 2
1 2 1 8
2 1 2 4

样例输出2

554580200

样例解释2

图拓扑为 12 之间连接了两条边。

1 所在连通块大小为 x

连接 12 的两条边不被看见的概率分别为 1913。因此 x2=1 当且仅当两条边都不被看见,概率为 127x2=4 以概率 2627 取到。

故答案为127×1+2627×4=359.

由于 9×55458020035(mod 998244353),因此你应该输出 554580200

Solution

注意到 n17 ,并且题目与集合的枚举(要求枚举与 1 连通的连通块)有关,容易想到用状压dp来做这道题。

解决这道题目的关键,在于状态如何设计以及如何进行转移

dpS 表示 最终与点 1 连通的点集为 S (其中对于边 (u,v) 的端点 uv,必须满足 uv 都在集合 S 当中),S 内部连通的概率S 之外的点的连通性不考虑在其中。

如何求出 dpS

——我们可以枚举符合条件的 S 的子集 T :考虑 S 中 包含点 1 的 真子集 TT 是连通的,而 TSTST)不能有连通的点

因此我们可以构建一个辅助数组 gg(A,B) 表示 点集 A 与点集 B 不连通的概率g(T,ST) 即为我们需要的。

如何求 g(A,B) ? 实际上,g(A,B) 其实就是 对于 uA,vB,边 (u,v) 必须不连通。题目中 p(u,v) 指的是 边 (u,v) 不连起来的概率。形式化地来描述,g(A,B)=uAvBp(u,v)

如果要预处理出 g(A,B) ,需要 O(4n) 的空间,显然不现实。

注意到 g(A,B) 其实就是 AB 之间的任意一条边都不连通。可以再设一个辅助数组 w(A) 表示集合 A 中任意一条边都不连起来, w(A) 比较好求

即有 g(A,B)=w(AB)w(A)×w(B) ,很容易理解:w(AB) 表示 集合 AB 中任意一条边都不连起来 ,从其中除去 w(A) ( A 中任意一条边都不连起来) 和 w(B) ( B 中任意一条边都不连起来) ,得到的结果就是 AB 之间的任意一条边都不连起来的概率。

最后我们可以得到 dpS 的转移方程 : dpS=11TTSmdpT×g(T,ST)

答案则是:ans=1SdpS×g(S,US)×|S|2dpSS 内部连通的概率,乘上 g(S,US) 表示不与 S 之外的点连通,乘上 |S|2 即为题目所求的期望。

附上代码:

#include <stdio.h>
#define N (17+5)
#define ST ((1<<17)+5)
typedef long long LL;
int n,m,sz;
LL mp[N][N];
LL w[ST],invw[ST];
LL dp[ST];
const LL p=998244353;
LL qpow(LL a,LL b,LL p){
	LL res=1;
	while(b){
		if(b&1) res=res*a%p;
		a=a*a%p;
		b>>=1;
	}
	return res;
}
LL inv(LL a,LL p){
	return qpow(a,p-2,p); 
}
void get_w(){                   //求w数组
	for(int k=0;k<=sz;k++){
		w[k]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			for(int k=0;k<=sz;k++){
				if(k&(1<<(i-1))||k&(1<<(j-1))) continue;
				int st=k;
				st|=(1<<(i-1)),st|=(1<<(j-1));      //(i,j)边不连通
				w[st]=(w[st]*mp[i][j])%p;           //乘进w[st]中
			}
		}
	}
	for(int k=0;k<=sz;k++){   //预处理出逆元
		invw[k]=inv(w[k],p);
	}
}
LL g(int a,int b){            //求辅助函数g
	return w[a+b]*invw[a]%p*invw[b]%p;
}
void solve(){
	for(int i=1;i<=sz;i+=2){                //枚举包含点1的集合S
		dp[i]=0;
		for(int j=(i-1)&i;j;j=(j-1)&i){     //枚举S的子集T
			if((j&1)==0) continue;          //T也要包含点1
			dp[i]=(dp[i]+(dp[j]*g(j,i-j))%p)%p;
		}
		dp[i]=(1-(dp[i])+p)%p;
	}
	LL ans=0;
	for(int i=1;i<=sz;i+=2){
		int k=0,st=i;
		while(st){
			if(st&1) k++;
			st>>=1;
		}
		ans=(ans+dp[i]*g(i,sz-i)%p*k%p*k%p)%p;
	}
	printf("%lld\n",ans);
}
int main(){
    freopen("karma.in","r",stdin);
    freopen("karma.out","w",stdout);
	scanf("%d%d",&n,&m);
	int u,v;
	LL x,y;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			mp[i][j]=1;
		}
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%lld%lld",&u,&v,&x,&y);
		if(u==v) continue; 
		mp[u][v]=mp[u][v]*x%p*inv((x+y)%p,p)%p;
		mp[v][u]=mp[u][v];
	}
	sz=(1<<n)-1;
	get_w();
	solve();
	return 0;
}
posted @   Truman_2022  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示