【XSY3951】简单的博弈题(博弈,dp,组合数,容斥)
题面
题解
对于贪心的对手的情况,田忌赛马即可。
对于随机的对手,发现使用任何策略都不影响结果。那我们只需要选一种自己数字的排列并固定下来,再去和对手的数字的 \(m!\) 种全排列匹配即可。
暴力枚举全排列是不可接受的,考虑自己选一种特殊的数字排列固定,以优化计算。
考虑将自己的数字排序后形成的排列为 \(a_1,a_2,\cdots,a_m\),不妨设对手的数字中有 \(s_i\) 个小于 \(a_i\)。
设 \(dp[i][j]\) 表示考虑了前 \(i\) 位,钦定我赢的次数为 \(j\),这 \(j\) 次赢的情况数。
如果上面 dp 状态的定义对于你来说比较难理解的话,相信看了下面的状态转移方程能帮助你理解:
然后设 \(f[j]\) 表示考虑完前 \(m\) 位,钦定我赢的次数为 \(j\),对手的数字排列的情况数。
容易得到:
设 \(g[j]\) 表示我赢的次数恰好为 \(j\),对手的数字排列的情况数。
易知:
那么可以得到:
而我们需要求的就是有多少种我们能赢的情况,即为:
其中最后一步用到了引理 1:
引理 1 证明:(来自 syh 巨佬(qq:3319203781),已获得转载许可)
在证明引理 1 之前我们先有一个引理 2:
\[\binom{n}{i}=\binom{n-1}{i}+\binom{n-1}{i-1} \]引理 2 证明:
公式化的理解:手动拆开式子然后约分。
从组合数上的意义理解:从 \(n\) 个物品中挑选出 \(i\) 个物品,相当于考虑是否最终挑选出第 \(i\) 个物品,如果不挑选出,那么方案数为 \(\dbinom{n-1}{i}\),如果挑选出,那么方案为 \(\dbinom{n-1}{i-1}\)。
那么:
令 \(p_{i-1}=\dbinom{n-1}{i-1}\),那么 \(p_{i-1}=\dbinom{n}{i}-\dbinom{n}{i+1}+p_{i+1}\)。
所以 \(p_{i-1}=\dbinom{n}{i}-\dbinom{n}{i+1}+\dbinom{n}{i+2}-\dbinom{n}{i+3}+\cdots\),即:
得证。
代码如下:
#include<bits/stdc++.h>
#define N 10010
using namespace std;
namespace modular
{
const int mod=998244353;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;
int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,m,a[N],b[N],f[N];
int fac[N],ifac[N];
int C(int n,int m)
{
if(m>n||n<0||m<0) return 0;
return mul(mul(fac[n],ifac[n-m]),ifac[m]);
}
int main()
{
n=read(),m=read();
fac[0]=1;
for(int i=1;i<=m;i++) fac[i]=mul(fac[i-1],i);
ifac[m]=poww(fac[m],mod-2);
for(int i=m;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
for(int i=1;i<=m;i++) a[i]=read();
sort(a+1,a+m+1);
while(n--)
{
int opt=read();
for(int i=1;i<=m;i++) b[i]=read();
sort(b+1,b+m+1);
if(opt)
{
bool flag=1;
for(int i=m/2+1,j=m;i>=1;i--,j--)
{
if(b[i]>a[j])
{
flag=0;
break;
}
}
printf("%d\n",flag);
}
else
{
for(int i=0;i<=m;i++) f[i]=0;
f[0]=1;
int tmp=0;
for(int i=1;i<=m;i++)
{
while(tmp<m&&b[tmp+1]<a[i]) tmp++;
for(int j=tmp;j>=1;j--)
f[j]=add(f[j],mul(f[j-1],tmp-(j-1)));
}
int ans=0;
for(int i=m/2+1;i<=m;i++)
{
int tmp=mul(mul(f[i],fac[m-i]),C(i-1,m/2));
if((i-m/2-1)&1) ans=dec(ans,tmp);
else ans=add(ans,tmp);
}
printf("%d\n",mul(ans,ifac[m]));
}
}
return 0;
}
/*
1 3
1 3 5
0 2 4 6
*/