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

CF482C Game with Strings

状压期望 dp+高维前缀和

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

fs=1+fs|2itot

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

考虑说不要固定一个字符串,因为 E=1nFi=1nFi,直接计算所有串的期望总和。那么转移就变成

fs=gs+fs|2itot

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

复杂度 O(m2m)

#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 @   Fire_Raku  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示