hhhqx
2024-11-15 16:34:03
模拟赛中的题是求排列数,不过改为求排列数是纯诈骗,需要从求概率的角度来看这题。
先来考虑
考虑
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int MAXM = (1ll << 20) + 3;
int n, m, pc[MAXM];
double c[MAXM], c1[MAXM], c2[MAXM]; // c 数组
double g[MAXM]; // g 数组
double f[MAXM], tmp[MAXM], _f[MAXM];
void ADD(double &x, double w){ x += w; }
void hdi_suf(double *A){
for(int i = 0; i < m; i++){
for(int s = 0; s < (1ll << m); s++) if((s >> i) & 1) ADD(A[s ^ (1ll << i)], A[s]);
}
}
void _hdi_suf(double *A){ // 高维后缀差分
for(int i = 0; i < m; i++){
for(int s = 0; s < (1ll << m); s++) if((s >> i) & 1) ADD(A[s ^ (1ll << i)], - A[s]);
}
}
void hdi_pre(double *A){
for(int i = 0; i < m; i++){
for(int s = 0; s < (1ll << m); s++) if((s >> i) % 2 == 0) ADD(A[s ^ (1ll << i)], A[s]);
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
int R = (1ll << m) - 1;
for(int s = 0; s < (1ll << m); s++){
for(int i = 0; i < m; i++) pc[s] += (s >> i) & 1;
}
for(int i = 1; i <= n; i++){
int S = 0;
for(int j = 0; j < m; j++){
char ch; cin >> ch, S += (ch == '1') * (1ll << j);
}
g[S]++, c1[S]++, c2[S]++;
}
hdi_pre(c1), hdi_suf(c2);
for(int s = 0; s < (1ll << m); s++){
c[s] = (s == 0 ? 0 : n - c1[s ^ R] - c2[s]);
if(c[s] < 0) return 1;
}
f[R] = 1;
hdi_suf(g); // 先对 g 做一遍后缀和
for(int i = m - 1; i >= 1; i--){
for(int s = 0; s < (1ll << m); s++) tmp[s] = f[s], f[s] = (c[s] == 0 ? 0 : f[s] / c[s]);
hdi_suf(f);
for(int s = 0; s < (1ll << m); s++) _f[s] = f[s] * g[s];
_hdi_suf(_f);
for(int s = 0; s < (1ll << m); s++){
if(pc[s] > i) f[s] = tmp[s];
if(pc[s] == i) f[s] = _f[s];
if(pc[s] < i) f[s] = 0;
}
}
vector<double> ans(m, 0);
for(int s = 1; s < (1ll << m); s++){
if(c[s] != 0) continue; // 只统计最终状态的概率
for(int i = 0; i < m; i++) if((s >> i) & 1) ADD(ans[i], f[s]);
}
cout << fixed << setprecision(10) << ans[0] << "\n";
return 0;
}