题解 刮痧

传送门

还算是思路比较正常

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

点击查看代码
#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 @ 2022-07-02 21:19  Administrator-09  阅读(7)  评论(0)    收藏  举报