题解 刮痧

传送门

还算是思路比较正常

首先发现这里 n15,考虑枚举哪些怪最终被打死了
发现攻击次数是定值,令 sumss 中怪物血量之和
那么就要求一个 gi,s 为对 s 集合中怪物攻击 i 次且均不打死的方案数
这相当于用 |s| 种颜色对 i 个石头染色,每次加入 lowbit 这种颜色即可
然后发现还需要计算出现每种情况的概率
fi,s 为当前死活状态为 s(0 未死,1 已死)的概率
转移考虑下一次是否击杀一只怪物
系数分别是 (isumsaj1)×1|s¯|1|s¯|
统计答案就枚举击杀怪物集合,乘上集合大小产生贡献即可
复杂度 O(2nnm+2nm2)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int a[N];

namespace force{
	double ans;
	void dfs(int u, double pre) {
		if (u>m) {
			int cnt=0;
			for (int i=1; i<=n; ++i) if (a[i]<=0) ++cnt;
			ans+=cnt*pre;
			return ;
		}
		int cnt=0;
		for (int i=1; i<=n; ++i) if (a[i]>0) ++cnt;
		pre/=cnt;
		for (int i=1; i<=n; ++i) if (a[i]>0) {
			--a[i];
			dfs(u+1, pre);
			++a[i];
		}
	}
	void solve() {
		dfs(1, 1);
		printf("%.5lf\n", ans);
	}
}

namespace task{
	int sum[1<<15], lg[N];
	double C[110][110], f[105][1<<15], g[105][1<<15];
	void solve() {
		int lim=1<<n;
		C[0][0]=1;
		for (int i=1; i<=m; ++i) {
			C[i][0]=1;
			for (int j=1; j<=i; ++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
		}
		for (int i=0; i<n; ++i) lg[1<<i]=i;
		sum[0]=0;
		for (int s=1; s<lim; ++s) sum[s]=sum[s-(s&-s)]+a[lg[s&-s]];
		f[0][0]=1;
		for (int i=0; i<m; ++i) {
			for (int s=0; s+1<lim; ++s) {
				for (int j=0; j<n; ++j) if (!(s&(1<<j)) && i-sum[s]>=a[j]-1)
					f[i+1][s|(1<<j)]+=C[i-sum[s]][a[j]-1]*f[i][s]/(n-__builtin_popcount(s));
				f[i+1][s]+=f[i][s]/(n-__builtin_popcount(s));
			}
		}
		for (int s=0; s<lim; ++s) g[0][s]=1;
		for (int i=1; i<=m; ++i) {
			for (int s=1,t; s<lim; ++s) {
				t=lg[s&-s];
				for (int j=0; j<a[t]&&j<=i; ++j)
					g[i][s]+=C[i][j]*g[i-j][s^(1<<t)];
			}
		}
		double ans=0;
		for (int s=0; s<lim; ++s) if (sum[s]<=m)
			ans+=f[m][s]*g[m-sum[s]][(~s)&(lim-1)]*__builtin_popcount(s);
		printf("%.5lf\n", ans);
	}
}

int main()
{
	freopen("scraping.in", "r", stdin);
	freopen("scraping.out", "w", stdout);
	
	n=read(); m=read();
	for (int i=0; i<n; ++i) a[i]=read();
	// force::solve();
	task::solve();
	
	return 0;
}
posted @   Administrator-09  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示