多校7 HDU5816 Hearthstone 状压DP+全排列

时间:2023-03-08 15:44:52
 多校7 HDU5816 Hearthstone 状压DP+全排列
题意:boss的PH为p,n张A牌,m张B牌。抽取一张牌,能胜利的概率是多少?
如果抽到的是A牌,当剩余牌的数目不少于2张,再从剩余牌里抽两张,否则全部拿完。
每次拿到一张B牌,对boss伤害B[i]的值
思路:dp[i]表示状态为i时的方案数
先处理出所有状态下的方案,再枚举每种状态,如果符合ans+=dp[i]*剩余数的全排列
当前集合里有a张A,b张B,那么还能取的牌数:a*-a-b+ #include <bits/stdc++.h>
using namespace std;
#define LL long long
const int inf = 0x3f3f3f3f;
const int MOD =;
const int N =;
#define clc(a,b) memset(a,b,sizeof(a))
const double eps = 1e-;
void fre() {freopen("in.txt","r",stdin);}
void freout() {freopen("out.txt","w",stdout);}
inline int read() {int x=,f=;char ch=getchar();while(ch>''||ch<'') {if(ch=='-') f=-;ch=getchar();}while(ch>=''&&ch<='') {x=x*+ch-'';ch=getchar();}return x*f;}
int n,m,p;
LL dp[<<];
int B[];
LL fac[]={};
int main(){
fac[]=;
for(int i=;i<;i++) fac[i]=fac[i-]*i;
int T,sum,a,b;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&p,&n,&m);
int N=n+m;
for(int i=;i<<<N;i++) dp[i]=;
for(int i=n;i<N;i++) scanf("%d",&B[i]); dp[]=,sum=,b=,a=;
for(int i=;i<<<N;i++){
if(dp[i]==) continue;
sum=,a=,b=;
for(int j=n;j<N;j++){
if(i&(<<j)){
sum+=B[j];
b++;
}
}
if(sum>=p)continue;
for(int j=;j<n;j++){
if(i&(<<j)){
a++;
}
}
if(a-b+<=) continue;
for(int j=;j<N;j++){
if((i&(<<j))==){
dp[i|(<<j)]+=dp[i];
}
}
}
LL Den=fac[N],Mol=;
for(int i=;i<(<<N);i++){
a=,b=,sum=;
for(int j=n;j<N;j++){
if(i&(<<j)){
b++;sum+=B[j];
}
}
if(sum<p) continue;
for(int j=;j<n;j++){
if(i&(<<j)){
a++;
}
}
Mol+=(LL)dp[i]*fac[N-b-a];
}
LL g=__gcd(Mol,Den);
printf("%I64d/%I64d\n",Mol/g,Den/g);
}
return ;
} 解法2标程:O(^m)
f[i][j]表示A拿i张,B拿j张的方案数
方法用f[i][j]表示A类牌和B类牌分别抽到i张和j张,且抽牌结束前保证i>=j的方案数,这个数组可以用O(n^)的dp预处理得到.
接下来枚举B类牌的每个子集,如果这个子集之和不小于P,用k表示子集的1的个数,将方案总数加上取到这个集合刚好A类卡片比B类卡片少一(过程结束)的方案数:f[k-][k] * C(n, k - ) * (k - )! * k! * (n + m – *k + )! .
如果子集包含了所有的B类卡片,则还需要再加上另一类取牌结束的情况,也就是取完所有牌,此时应加上的方案数为f[n][m] * n! * m! .
最后的总方案数除以(n+m)!就是答案. #include <cstdio>
typedef long long lli; int bc[<<], sum[<<], tmp[<<];
int C[][];
lli f[][], fact[]; lli gcd(lli a, lli b)
{
if (b == ) return a;
return gcd(b, a % b);
} void init()
{
int i, j;
bc[] = ;
for (i=; i<(<<); i++) bc[i] = bc[i^(i&-i)] + ;
fact[] = ;
for (i=; i<=; i++) fact[i] = fact[i-] * i;
C[][] = ;
for (i=; i<=; i++)
{
C[i][] = C[i][i] = ;
for (j=; j<i; j++) C[i][j] = C[i-][j] + C[i-][j-];
}
f[][] = f[][] = ;
for (i=; i<=; i++)
{
f[i][] = ;
for (j=; j<i; j++) f[i][j] = f[i-][j] + f[i][j-];
f[i][i] = f[i][i+] = f[i][i-];
}
} int main()
{
lli a, b, d;
int p, n, m, t, i;
init();
scanf("%d", &t);
while (t --)
{
scanf("%d %d %d", &p, &n, &m);
for (i=; i<m; i++) scanf("%d", &tmp[<<i]);
sum[] = ;
for (i=; i<(<<m); i++) sum[i] = sum[i^(i&-i)] + tmp[i&-i];
a = ;
for (i=; i<(<<m); i++)
{
if (sum[i] >= p && bc[i] <= n + )
{
a += C[n][bc[i]-] * f[bc[i]-][bc[i]] * fact[bc[i]-] * fact[bc[i]] * fact[n+m-*bc[i]+];
if (bc[i] == m && bc[i] < n + ) a += f[n][m] * fact[n] * fact[m];
}
}
b = fact[n+m];
d = gcd(a, b);
printf("%I64d/%I64d\n", a / d, b / d);
}
return ;
}