Topcoder RandomPaintingOnABoard(min-max容斥)
Topcoder RandomPaintingOnABoard(min-max容斥)
题目大意
给定一个 \(n * m\) 的矩阵,每个元素是 \(0 \to9\) 的数字,每次以 \(\frac {p[i][j]}{sum}\) 的概率将第 i 块染黑(每次染一个块),想让每行每列都有至少一个块被染色,求期望次数
数据范围
\(n * m \le 200, p[i][j] \le 9\)
解题思路
用 \(min-max\) 容斥将最后一个被染色转化成第一个被染色,我们有
\[\max(S)=\sum_{T \subset S}(-1)^{|T|+1}\min(T) \\
=\sum_{T \subset S}(-1)^{|T|+1}\sum_{i=0}P(i)*i\\
=\sum_{T \subset S}(-1)^{|T|+1}\sum_{i=0}P(x \ge i)\\
=\sum_{T \subset S}(-1)^{|T|+1}\sum_{i=0}(1-\frac {sum(T)}{Sum})^i\\
=\sum_{T \subset S}(-1)^{|T|+1}\frac {Sum}{sum(T)}\\
\]
好了可以愉快的 dp 了,假设 \(n \le m\),显然可以暴力枚举 n,然后套路的 dp 即可
代码奉上
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '\n')
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 205;
const int P = 998244353;
#define LD long double
ll p[N][N], bs[N], sum;
LD f[N][N*9]; ll m, n;
char s[N];
class RandomPaintingOnABoard{
public:
LD expectedSteps(const vector<string> &prob) {
n = prob.size(), m = prob[0].size();
for (int i = 1;i <= n; i++) {
for (int j = 1;j <= m; j++)
if (n < m) p[i][j] = prob[i-1][j-1] - '0', sum += p[i][j];
else p[j][i] = prob[i-1][j-1] - '0', sum += p[j][i];
}
if (n > m) swap(n, m);
LD ans = 0;
int all = (1 << n) - 1;
for (int i = 0;i <= all; i++) {
ll sz = 0, res = 0;
for (int j = 1;j <= n; j++) {
if (i & (1 << (j - 1))) {
bs[j] = 1, sz++;
for (int k = 1;k <= m; k++)
res += p[j][k];
}
else bs[j] = 0;
}
memset(f, 0, sizeof(f)); f[0][res] = 1;
for (int j = 1;j <= m; j++) {
int tp = 0;
for (int k = 1;k <= n; k++)
if (!bs[k]) tp += p[k][j];
for (int k = 0;k <= res; k++)
f[j][k + tp] -= f[j-1][k], f[j][k] += f[j-1][k];
res += tp;
}
for (int k = 1;k <= res; k++) {
if (sz & 1) ans += f[m][k] * sum / k;
else ans -= f[m][k] * sum / k;
}
// printf ("%.10Lf\n", ans);
}
return ans;
}
};
//int main(){
// vector<string> prob=
// {"000000000000001000000",
// "888999988889890999988",
// "980016505602315105399",
// "889898998889980999898",
// "988812312398988089999",
// "998888991151513889998",
// "998988999898990889899",
// "135135489465125616545",
// "958646813213300035186"};
// RandomPaintingOnABoard tmp;
// LD ans=tmp.expectedSteps(prob);
// printf("%.10Lf\n",ans);
// return 0;
//}