Loading

CF482C Game with Strings (状压期望 dp+高维前缀和)

CF482C Game with Strings

状压期望 dp+高维前缀和

考虑固定一个要猜出的字符串,然后考虑期望 dp,状压目前已经猜了的字符位置,设 \(f_{s}\) 表示已经猜了的字符位置状态为 \(s\),最少还需要猜几次的期望值。那么转移枚举下一次要猜的位置 \(i\),有

\[f_{s}=1+\sum\frac{f_{s|2^i}}{tot} \]

\(tot\) 表示剩余还没猜的位置。如果 \(s\) 的时候已经猜出,那么 \(f_s=0\)。可以预处理出状态 \(s\) 时,还不能分辨的字符串集合为 \(g_{s}\)。复杂度大概是 \(O(nm2^m)\) 的。

考虑说不要固定一个字符串,因为 \(E=\sum\frac{1}{n}F_{i}=\frac{1}{n}\sum F_{i}\),直接计算所有串的期望总和。那么转移就变成

\[f_{s}=g_s+\sum\frac{f_{s|2^i}}{tot} \]

\(g_s\) 的求法,考虑一个猜字符的集合无法分辨出唯一一个字符串,那么一定存在另一个字符串的对应位置都与之相同。考虑枚举这么两个字符串,并求其极大相同字符集记录在 \(g_s\) 中。此时剩下的非极大相同字符集一定包含于已求的极大相同字符集中,高维前缀和求每个集合的超集即可。

复杂度 \(O(m2^m)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 52, M = 22;
int n, m, lim;
char s[N][M];
double f[1 << M];
i64 g[1 << M];

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;
	for(int i = 0; i < n; i++) {
		std::cin >> s[i];
	}
	if(n == 1) {
		std::cout << "0\n";
		return 0;
	}

	m = strlen(s[0]), lim = (1 << m) - 1;
	g[0] = (1LL << n) - 1;
	for(int i = 0; i < n; i++) {
		for(int j = i + 1; j < n; j++) {
			int sta = 0;
			for(int k = 0; k < m; k++) {
				if(s[i][k] == s[j][k]) sta |= (1 << k);
			}
			g[sta] |= (1LL << i) | (1LL << j);
		}
	}

	for(int i = 0; i < m; i++) {
		for(int s = lim; ~s; s--) {
			if(!(s & (1 << i))) g[s] |= g[s | (1 << i)];
		}
	}

	for(int s = lim; ~s; s--) {
		if(!g[s]) continue;
		for(int i = 0; i < m; i++) {
			if(!(s & (1 << i))) f[s] += f[s | (1 << i)];  
		}
		f[s] /= (m - __builtin_popcountll(s));
		f[s] += __builtin_popcountll(g[s]);
	}
	std::cout << std::fixed << std::setprecision(15) << f[0] / n << "\n";

	return 0;
}
posted @ 2024-06-07 00:19  Fire_Raku  阅读(6)  评论(0编辑  收藏  举报