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

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

题目描述

题目链接

题目

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

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

具体来说:

  • \(i\) 条边连接的两个端点分别为 \(u_i\)\(v_i\)
  • 这条边上落下了许多灰尘,因此云浅有 \(p_i\) 的概率看不到这条边。这里保证 \(0≤p_i≤1,p_i∈Q\)

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

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

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

输入格式

第一行两个正整数 \(n,m\)

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

  • \(u_i,v_i\) 表示这条边的两个端点。
  • 云浅看不到这条边的概率即为 \(p_i=\frac{x_i}{x_i+y_i}\)

输出格式

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

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

更具体地,设答案为 \(\frac{A}{B}\),其中 \(gcd⁡(A,B)=1\),你需要输出一个 \(C\) 使得 \(B×C≡A ( mod \text{ }998244353)\)

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

数据范围

对于 \(100\%\) 的数据,\(1≤n≤17,1≤m≤\frac{n(n−1)}{2},1≤x_i,y_i<998244353,x_i+y_i≠998244353\)

测试点编号 \(n\) \(m\)
\(1∼2\) \(=8\) \(≤12\)
\(3∼4\) \(=9\) \(≤12\)
\(5∼6\) \(=10\) \(≤15\)
\(7∼8\) \(=11\) \(≤25\)
\(9∼10\) \(=12\) \(≤30\)
\(11∼12\) \(=13\) \(≤40\)
\(13∼14\) \(=14\) \(≤\frac{n(n−1)}{2}\)
\(15∼16\) \(=15\) \(≤\frac{n(n−1)}{2}\)
\(17∼18\) \(=16\) \(≤\frac{n(n−1)}{2}\)
\(19∼20\) \(=17\) \(≤\frac{n(n−1)}{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

图拓扑为 \(1\)\(2\) 之间连接了两条边。

\(1\) 所在连通块大小为 \(x\)

连接 \(1\)\(2\) 的两条边不被看见的概率分别为 \(\frac{1}{9}\)\(\frac{1}{3}\)。因此 \(x^2=1\) 当且仅当两条边都不被看见,概率为 \(\frac{1}{27}\)\(x^2=4\) 以概率 \(\frac{26}{27}\) 取到。

故答案为\(\frac{1}{27}×1+\frac{26}{27}×4=\frac{35}{9}\).

由于 \(9×554580200≡35 ( mod \text{ }998244353)\),因此你应该输出 \(554580200\)

Solution

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

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

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

如何求出 \(dp_S\)

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

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

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

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

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

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

最后我们可以得到 \(dp_S\) 的转移方程 : \(dp_S=1-\displaystyle \sum^{m}_{1\in T \bigwedge T \subsetneq S} dp_T\times g(T,S-T)\)

答案则是:\(ans=\displaystyle \sum^{}_{1\in S} dp_S \times g(S,U-S) \times |S|^2\)\(dp_S\)\(S\) 内部连通的概率,乘上 \(g(S,U-S)\) 表示不与 \(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 @ 2023-07-19 16:15  Truman_2022  阅读(28)  评论(0编辑  收藏  举报