[XSY] 简单的博弈题(博弈+dp+组合数+容斥)

25 篇文章 1 订阅
4 篇文章 0 订阅

简单的博弈题
对于贪心的对手,显然用最大的一半和他最小的一半比较判断是否全胜。(这不就是田忌赛马吗)

对于随机的对手,先考虑暴力怎么做:

void check(int x,int w){
    if(x>m){
        res+=(w>=m/2+1);
        return;
    }
    for(int i=1;i<=m;i++){
        if(visb[i]) continue;
        visb[i]=1;
        if(xa[x]>b[i]) check(x+1,w+1);
        else check(x+1,w);
        visb[i]=0;
    }
}
void dfs(int x){
    if(x>m){
        res=0;
        check(1,0);
        ans=max(ans,res);
        return;
    }
    for(int i=1;i<=m;i++){
        if(visa[i]) continue;
        visa[i]=1;
        xa[x]=a[i];
        dfs(x+1);
        visa[i]=0;
    }
}
int main(){
	inv[0]=inv[1]=1;
    for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<N;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%lld",&a[i]);
    while(n--){
        scanf("%d",&op);
        for(int i=1;i<=m;i++) 
            scanf("%lld",&b[i]);
        ans=0;
        dfs(1);
        ans=ans*inv[m]%mod;
        printf("%lld\n",ans);
   }
}

由暴力中我们发现一件事:由于 a [ ] , b [ ] a[],b[] a[],b[]的配对情况固定, 第 一 个 人 使 用 任 何 策 略 都 不 影 响 他 获 胜 的 概 率 \color{Red}第一个人使用任何策略都不影响他获胜的概率 使
也就是说我们只需选一种自己数字的排列并固定下来,再去和对手的数字的 m ! m! m!种全排列匹配即可。

暴力枚举全排列显然是不可接受的,考虑从小到大考虑自己的每个数,用 f i , j f_{i,j} fi,j表示考虑了前 i i i小的数,给其中 j j j个匹配了比他小的数字的方案数

思考 f i , j f_{i,j} fi,j的转移:
若给第 i i i个数匹配比他小的数字:
f i , j ← f i − 1 , j − 1 × ( p − ( j − 1 ) ) f_{i,j}\leftarrow f_{i-1,j-1}\times(p-(j-1)) fi,jfi1,j1×(p(j1))
(其中 p p p表示对手的数字中有 p p p个小于 a [ i ] a[i] a[i]
若给第 i i i个数匹配比他大的数字:
f i , j ← f i − 1 , j × q f_{i,j}\leftarrow f_{i-1,j}\times q fi,jfi1,j×q
(其中 q q q表示此时对手的数字中还剩 q q q个比 a [ i ] a[i] a[i]大的未匹配)

问题来了,要算出 q q q,必须知道前 i − 1 i-1 i1个数具体匹配了对手的哪些数,那不就跟暴力一样了吗?

对此,我们的解决方法是
若不给第 i i i个数匹配比他小的数字,那么这个数先不匹配,即 f i , j = f i − 1 , j + f i − 1 , j − 1 × ( p − ( j − 1 ) ) f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\times(p-(j-1)) fi,j=fi1,j+fi1,j1×(p(j1))
在考虑完自己的所有数后,我们再给还没匹配的数统一匹配:
f m , j = f m , j × ( m − j ) ! f_{m,j}=f_{m,j}\times(m-j)! fm,j=fm,j×(mj)!
但这样我们就不能保证所有数中 恰 好 \color{Blue}恰好 j j j个数匹配到了比他小的数,事实上, f m , j 表 示 所 有 数 中 \color{Red}f_{m,j}表示所有数中 fm,j 至 少 \color{Blue}至少 有 j 个 数 匹 配 到 了 比 他 小 的 数   的 方 案 数 \color{Red}有j个数匹配到了比他小的数\space的方案数 j 

根据广义容斥原理/二项式反演
g ( k ) g(k) g(k)表示所有数中恰好 k k k个数匹配到了比他小的数 的方案数,
f ( k ) f(k) f(k)表示所有数中至少 k k k个数匹配到了比他小的数 的方案数,
a n s 表 示 自 己 能 赢 的 方 案 数 ans表示自己能赢的方案数 ans
∵ f ( k ) = ∑ i = k m C i k g ( i ) \because f(k)=\sum_{i=k}^{m}C_{i}^{k}g(i) f(k)=i=kmCikg(i) ( 因 此 a n s ≠ f ( ⌊ m + 1 2 ⌋ ) ) \color{Red}(因此ans\not=f(\left\lfloor\frac{m+1}{2}\right\rfloor)) ans=f(2m+1)
∴ g ( k ) = ∑ i = k m ( − 1 ) i − k C i k f ( i ) \therefore g(k)=\sum_{i=k}^{m}(-1)^{i-k}C_{i}^{k}f(i) g(k)=i=km(1)ikCikf(i)

w = ⌊ m + 1 2 ⌋ w=\left\lfloor\frac{m+1}{2}\right\rfloor w=2m+1
a n s = ∑ k = w m g ( k ) = ∑ k = w m ∑ i = k m ( − 1 ) i − k C i k f ( i ) = ∑ i = w m f ( i ) ∑ k = w i ( − 1 ) i − k C i k ans=\sum_{k=w}^{m}g(k)=\sum_{k=w}^{m}\sum_{i=k}^{m}(-1)^{i-k}C_{i}^{k}f(i)=\color{Red}\sum_{i=w}^{m}f(i)\sum_{k=w}^{i}(-1)^{i-k}C_{i}^{k} ans=k=wmg(k)=k=wmi=km(1)ikCikf(i)=i=wmf(i)k=wi(1)ikCik
事实上,优化到这里已经能AC

但这个式子还可以再优化:
引理1: ∑ i = w n ( − 1 ) n − i C n i = ( − 1 ) n − w C n − 1 w − 1 \color{Red}\sum_{i=w}^{n}(-1)^{n-i}C_{n}^{i}=(-1)^{n-w}C_{n-1}^{w-1} i=wn(1)niCni=(1)nwCn1w1
证明1:

引理2: C n i = C n − 1 i − 1 + C n − 1 i C_{n}^{i}=C_{n-1}^{i-1}+C_{n-1}^{i} Cni=Cn1i1+Cn1i
证明2:显然

由引理2, C n − 1 i − 1 = C n i − C n − 1 i C_{n-1}^{i-1}=C_{n}^{i}-C_{n-1}^{i} Cn1i1=CniCn1i
∵ C n i + 1 = C n − 1 i + C n − 1 i + 1 \because C_{n}^{i+1}=C_{n-1}^{i}+C_{n-1}^{i+1} Cni+1=Cn1i+Cn1i+1
∴ C n − 1 i = C n i + 1 − C n − 1 i + 1 \therefore C_{n-1}^{i}=C_{n}^{i+1}-C_{n-1}^{i+1} Cn1i=Cni+1Cn1i+1
∴ C n − 1 i − 1 = C n i − ( C n i + 1 − C n − 1 i + 1 ) = C n i − C n i + 1 + C n − 1 i + 1 \therefore C_{n-1}^{i-1}=C_{n}^{i}-(C_{n}^{i+1}-C_{n-1}^{i+1})=C_{n}^{i}-C_{n}^{i+1}+C_{n-1}^{i+1} Cn1i1=Cni(Cni+1Cn1i+1)=CniCni+1+Cn1i+1
p i − 1 = C n − 1 i − 1 p_{i-1}=C_{n-1}^{i-1} pi1=Cn1i1,那么
p i − 1 = C n i − C n i + 1 + C n − 1 i + 1 = C n i − C n i + 1 + p i + 1 p_{i-1}=C_{n}^{i}-C_{n}^{i+1}+C_{n-1}^{i+1}=C_{n}^{i}-C_{n}^{i+1}+p_{i+1} pi1=CniCni+1+Cn1i+1=CniCni+1+pi+1
∵ p i + 1 = C n i + 2 − C n i + 3 + p i + 3 \because p_{i+1}=C_{n}^{i+2}-C_{n}^{i+3}+p_{i+3} pi+1=Cni+2Cni+3+pi+3
∴ p i − 1 = C n i − C n i + 1 + C n i + 2 − C n i + 3 + p i + 3 = C n i − C n i + 1 + C n i + 2 − C n i + 3 + C n i + 4 − C n i + 5 + . . . C n n \therefore p_{i-1}=C_{n}^{i}-C_{n}^{i+1}+C_{n}^{i+2}-C_{n}^{i+3}+p_{i+3}=C_{n}^{i}-C_{n}^{i+1}+C_{n}^{i+2}-C_{n}^{i+3}+C_{n}^{i+4}-C_{n}^{i+5}+...C_{n}^{n} pi1=CniCni+1+Cni+2Cni+3+pi+3=CniCni+1+Cni+2Cni+3+Cni+4Cni+5+...Cnn
∴ ∑ i = w n ( − 1 ) n − i C n i = ( − 1 ) n − w p w − 1 = ( − 1 ) n − w C n − 1 w − 1 \therefore \sum_{i=w}^{n}(-1)^{n-i}C_{n}^{i}=(-1)^{n-w}p_{w-1}=(-1)^{n-w}C_{n-1}^{w-1} i=wn(1)niCni=(1)nwpw1=(1)nwCn1w1
证毕

∴ a n s = ∑ i = w m f ( i ) ∑ k = w i ( − 1 ) i − k C i k = ∑ i = w m ( − 1 ) i − w C i − 1 w − 1 f ( i ) \therefore ans=\sum_{i=w}^{m}f(i)\sum_{k=w}^{i}(-1)^{i-k}C_{i}^{k}=\sum_{i=w}^{m}(-1)^{i-w}C_{i-1}^{w-1}f(i) ans=i=wmf(i)k=wi(1)ikCik=i=wm(1)iwCi1w1f(i)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10010;
const int mod=998244353;
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*10+ch-'0';ch=getchar();}
    return x*f;
}
int f[N],a[N],b[N],fac[N],inv[N];
int power(int a,int b){
    int res=1;
    while(b){
        if(b&1) res=1ll*res*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return res;
}
void init(int n){
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    inv[n]=power(fac[n],mod-2);
    for(int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
}
int C(int n,int m){
    return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int n,m,op;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) a[i]=read();
    sort(a+1,a+m+1);
    init(m);
    while(n--){
        scanf("%d",&op);
        for(int i=1;i<=m;i++) b[i]=read();
        sort(b+1,b+m+1);
        if(op==1){
            int ans=1;
            for(int i=1;i<=(m+1)/2;i++){
                if(b[i]>a[i+(m-1)/2]){
                    ans=0;break;
                }
            }
            printf("%d\n",ans);
        } 
        else{
            memset(f,0,sizeof(f));
            f[0]=1;
            for(int i=1;i<=m;i++){
                int p=lower_bound(b+1,b+m+1,a[i])-b-1;
                for(int j=p;j>=1;j--)
                    f[j]=(f[j]+1ll*f[j-1]*(p-(j-1)))%mod;
            }
            for(int i=1;i<=m;i++)
                f[i]=1ll*f[i]*fac[m-i]%mod;
            int ans=0,tag=0;
            for(int i=(m+1)/2;i<=m;i++){
                int sum=1ll*C(i-1,(m-1)/2)*f[i]%mod;
                if(tag) sum=mod-sum;
                ans=(ans+sum)%mod;
                tag^=1;
            }
            ans=1ll*ans*inv[m]%mod;
            printf("%lld\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值