其他分享
首页 > 其他分享> > ICPC2021网络赛第二场 K Meals 概率 dp

ICPC2021网络赛第二场 K Meals 概率 dp

作者:互联网

第\(i\)个人能选\(j\)​个物品,当且仅当前\(i-1\)个人没选第\(j\)个物品,且\(i\)的喜好序列中,第\(j\)个物品是未被选走的里排最前面的。

第\(i\)个人喜好序列中第\(j\)个物品排在任意一个位置的概率均为\(a_{ij}/\sum_{k=1}^{n}a_{ik}\),

前\(i\)​个人选的物品的集合为\(S\)​​的概率,记为\(p(i,S)\)​,则第\(i\)​个人选第\(j\)​个物品的概率为

\[\sum_{S,S中有i-1个1}[j\notin S]\times p(i-1, S)\times \frac{a[i][j]}{\sum_{k=1}^na[i][k]\times [k \notin S]} \]

,知道\(p\)​之后后面的东西可以\(O(2^n n)\)​​​算,

\[p(i,S) = \sum_{j=1}^{n}[j\in S,S中有i个1]\times p(i-1,S-(1<<(j-1)))\times \frac{a[i][j]}{a[i][j]+\sum_{k=1}^na[i][k]\times [k \notin S]} \]

数组开小wa半天,没有线性求逆元会tle

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20 + 7;
#define ll long long
const ll md = 998244353;
ll n, a[maxn][maxn], sum[maxn][maxn], p[maxn][1048577], ans[maxn][maxn], cnt[1048577]; 
ll rd() {
	ll s = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
	return s * f;
}
ll ksm(ll a, ll b) {
	ll res = 1;
	while (b) {
		if (b & 1ll) res = res * a % md;
		a = a * a % md;
		b >>= 1ll;
	}
	return res;
}
ll inv[2007];
int main() {
	n = rd();
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			a[i][j] = rd();
			//sum[i][j] = (sum[i][j-1] + a[i][j]) % md;
		}
	}
	inv[0] = inv[1] = 1;
	for (int i = 2; i <= 2000; i++) 
		inv[i] = inv[md % i] * (md - md / i) % md;
	//for (int i = 1; i <= n; i++) ans[1][i] = a[1][i] * ksm(sum[1][n], md - 2) % md;
	//p[i][S]表示前i个人已选集合为S的概率。
	for (int S = 1; S < (1 << n); S++) {
		if (S & 1) cnt[S] = cnt[S>>1] + 1;
		else cnt[S] = cnt[S>>1];
		//p[0][S] = 1;
	}
	p[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int S = 0; S < (1 << n); S++) {
			ll sum0 = 0;
			for (int j = 0; j < n; j++) {
				if (S & (1 << j)) continue;
				sum0 = (sum0 + a[i][j+1]) % md;
			}
			if (cnt[S] == i-1) {
				for (int j = 1; j <= n; j++) {
					if ((1 << (j-1)) & S) continue;
						ans[i][j] = (ans[i][j] + p[i-1][S] * a[i][j] % md * inv[sum0] % md) % md;
				}
			}
			if (cnt[S] == i) {
				for (int j = 1; j <= n; j++) {
					if (S & (1 << (j-1))) {						
						p[i][S] = (p[i][S] + p[i-1][S^(1<<(j-1))]*a[i][j]%md*inv[sum0+a[i][j]]%md)%md;
					}
				}
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			printf("%lld", ans[i][j]);
			if (j != n) printf(" ");
		}
		if (i != n)
			printf("\n");
	}
	return 0;
}

标签:ICPC2021,int,ll,times,maxn,sum,物品,Meals,dp
来源: https://www.cnblogs.com/YjmStr/p/15346212.html