【XSY3951】简单的博弈题(博弈,dp,组合数,容斥)

20 篇文章 0 订阅
7 篇文章 0 订阅

题面

简单的博弈题

题解

对于贪心的对手的情况,田忌赛马即可。

对于随机的对手,发现使用任何策略都不影响结果。那我们只需要选一种自己数字的排列并固定下来,再去和对手的数字的 m ! m! m! 种全排列匹配即可。

暴力枚举全排列是不可接受的,考虑自己选一种特殊的数字排列固定,以优化计算。

考虑将自己的数字排序后形成的排列为 a 1 , a 2 , ⋯   , a m a_1,a_2,\cdots,a_m a1,a2,,am,不妨设对手的数字中有 s i s_i si 个小于 a i a_i ai

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示考虑了前 i i i 位,钦定我赢的次数为 j j j,这 j j j 次赢的情况数。

如果上面 dp 状态的定义对于你来说比较难理解的话,相信看了下面的状态转移方程能帮助你理解:

d p [ 0 ] [ 0 ] = 1 d p [ i ] [ 0 ] = 1 if ⁡ i ≥ 1 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] × ( s i − ( j − 1 ) ) if ⁡ i , j ≥ 1 \begin{aligned} &dp[0][0]=1\\ &dp[i][0]=1\qquad\operatorname{if}i\geq 1\\ &dp[i][j]=dp[i-1][j]+dp[i-1][j-1]\times (s_i-(j-1))\qquad\operatorname{if }i,j\geq 1 \end{aligned} dp[0][0]=1dp[i][0]=1ifi1dp[i][j]=dp[i1][j]+dp[i1][j1]×(si(j1))ifi,j1

然后设 f [ j ] f[j] f[j] 表示考虑完前 m m m 位,钦定我赢的次数为 j j j,对手的数字排列的情况数。

容易得到:

f [ j ] = ( m − j ) ! × d p [ m ] [ j ] f[j]=(m-j)!\times dp[m][j] f[j]=(mj)!×dp[m][j]

g [ j ] g[j] g[j] 表示我赢的次数恰好 j j j,对手的数字排列的情况数。

易知:

f [ w ] = ∑ i = w m ( i w ) g [ i ] f[w]=\sum_{i=w}^{m}\binom{i}{w}g[i] f[w]=i=wm(wi)g[i]

那么可以得到:

g [ w ] = ∑ i = w m ( − 1 ) i − w ( i w ) f [ i ] g[w]=\sum_{i=w}^m(-1)^{i-w}\binom{i}{w}f[i] g[w]=i=wm(1)iw(wi)f[i]

而我们需要求的就是有多少种我们能赢的情况,即为:

a n s = ∑ w = m + 1 2 m g [ w ] = ∑ w = m + 1 2 m ∑ i = w m ( − 1 ) i − w ( i w ) f [ i ] = ∑ i = m + 1 2 m f [ i ] ∑ w = m + 1 2 i ( − 1 ) i − w ( i w ) = ∑ i = m + 1 2 m f [ i ] ( i − 1 m − 1 2 ) \begin{aligned} ans=&\sum_{w=\frac{m+1}{2}}^{m}g[w]\\ =&\sum_{w=\frac{m+1}{2}}^{m}\sum_{i=w}^m(-1)^{i-w}\binom{i}{w}f[i]\\ =&\sum_{i=\frac{m+1}{2}}^{m}f[i]\sum_{w=\frac{m+1}{2}}^{i}(-1)^{i-w}\binom{i}{w}\\ =&\sum_{i=\frac{m+1}{2}}^{m}f[i]\binom{i-1}{\frac{m-1}{2}} \end{aligned} ans====w=2m+1mg[w]w=2m+1mi=wm(1)iw(wi)f[i]i=2m+1mf[i]w=2m+1i(1)iw(wi)i=2m+1mf[i](2m1i1)

其中最后一步用到了引理 1:

∑ j = i n ( − 1 ) j − i ( n j ) = ( n − 1 i − 1 ) \sum_{j=i}^n (-1)^{j-i}\binom{n}{j}=\binom{n-1}{i-1} j=in(1)ji(jn)=(i1n1)

引理 1 证明:(来自 syh 巨佬(qq:3319203781),已获得转载许可)

在证明引理 1 之前我们先有一个引理 2:

( n i ) = ( n − 1 i ) + ( n − 1 i − 1 ) \binom{n}{i}=\binom{n-1}{i}+\binom{n-1}{i-1} (in)=(in1)+(i1n1)

引理 2 证明:

公式化的理解:手动拆开式子然后约分。

从组合数上的意义理解:从 n n n 个物品中挑选出 i i i 个物品,相当于考虑是否最终挑选出第 i i i 个物品,如果不挑选出,那么方案数为 ( n − 1 i ) \dbinom{n-1}{i} (in1),如果挑选出,那么方案为 ( n − 1 i − 1 ) \dbinom{n-1}{i-1} (i1n1)

那么:

( n − 1 i − 1 ) = ( n i ) − ( n − 1 i ) = ( n i ) − ( ( n i + 1 ) − ( n − 1 i + 1 ) ) = ( n i ) − ( n i + 1 ) + ( n − 1 i + 1 ) \begin{aligned} &\binom{n-1}{i-1}\\ =&\binom{n}{i}-\binom{n-1}{i}\\ =&\binom{n}{i}-\left(\binom{n}{i+1}-\binom{n-1}{i+1}\right)\\ =&\binom{n}{i}-\binom{n}{i+1}+\binom{n-1}{i+1} \end{aligned} ===(i1n1)(in)(in1)(in)((i+1n)(i+1n1))(in)(i+1n)+(i+1n1)

p i − 1 = ( n − 1 i − 1 ) p_{i-1}=\dbinom{n-1}{i-1} pi1=(i1n1),那么 p i − 1 = ( n i ) − ( n i + 1 ) + p i + 1 p_{i-1}=\dbinom{n}{i}-\dbinom{n}{i+1}+p_{i+1} pi1=(in)(i+1n)+pi+1

所以 p i − 1 = ( n i ) − ( n i + 1 ) + ( n i + 2 ) − ( n i + 3 ) + ⋯ p_{i-1}=\dbinom{n}{i}-\dbinom{n}{i+1}+\dbinom{n}{i+2}-\dbinom{n}{i+3}+\cdots pi1=(in)(i+1n)+(i+2n)(i+3n)+,即:

( n − 1 i − 1 ) = p i − 1 = ∑ j = i n ( − 1 ) j − i ( n j ) \dbinom{n-1}{i-1}=p_{i-1}=\sum_{j=i}^n(-1)^{j-i}\dbinom{n}{j} (i1n1)=pi1=j=in(1)ji(jn)

得证。

代码如下:

#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
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值