简单的博弈题
对于贪心的对手,显然用最大的一半和他最小的一半比较判断是否全胜。(这不就是田忌赛马吗)
对于随机的对手,先考虑暴力怎么做:
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!种全排列匹配即可。
暴力枚举全排列显然是不可接受的,考虑从小到大考虑自己的每个数,用fi,jf_{i,j}fi,j表示考虑了前iii小的数,给其中jjj个匹配了比他小的数字的方案数。
思考fi,jf_{i,j}fi,j的转移:
若给第iii个数匹配比他小的数字:
fi,j←fi−1,j−1×(p−(j−1))f_{i,j}\leftarrow f_{i-1,j-1}\times(p-(j-1))fi,j←fi−1,j−1×(p−(j−1))
(其中ppp表示对手的数字中有ppp个小于a[i]a[i]a[i])
若给第iii个数匹配比他大的数字:
fi,j←fi−1,j×qf_{i,j}\leftarrow f_{i-1,j}\times qfi,j←fi−1,j×q
(其中qqq表示此时对手的数字中还剩qqq个比a[i]a[i]a[i]大的未匹配)
问题来了,要算出qqq,必须知道前i−1i-1i−1个数具体匹配了对手的哪些数,那不就跟暴力一样了吗?
对此,我们的解决方法是
若不给第iii个数匹配比他小的数字,那么这个数先不匹配,即fi,j=fi−1,j+fi−1,j−1×(p−(j−1))f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\times(p-(j-1))fi,j=fi−1,j+fi−1,j−1×(p−(j−1))
在考虑完自己的所有数后,我们再给还没匹配的数统一匹配:
fm,j=fm,j×(m−j)!f_{m,j}=f_{m,j}\times(m-j)!fm,j=fm,j×(m−j)!
但这样我们就不能保证所有数中恰好\color{Blue}恰好恰好有jjj个数匹配到了比他小的数,事实上,fm,j表示所有数中\color{Red}f_{m,j}表示所有数中fm,j表示所有数中至少\color{Blue}至少至少有j个数匹配到了比他小的数的方案数\color{Red}有j个数匹配到了比他小的数\space的方案数有j个数匹配到了比他小的数 的方案数
根据广义容斥原理/二项式反演,
设g(k)g(k)g(k)表示所有数中恰好有kkk个数匹配到了比他小的数 的方案数,
f(k)f(k)f(k)表示所有数中至少有kkk个数匹配到了比他小的数 的方案数,
ans表示自己能赢的方案数ans表示自己能赢的方案数ans表示自己能赢的方案数
∵f(k)=∑i=kmCikg(i)\because f(k)=\sum_{i=k}^{m}C_{i}^{k}g(i)∵f(k)=∑i=kmCikg(i) (因此ans≠f(⌊m+12⌋))\color{Red}(因此ans\not=f(\left\lfloor\frac{m+1}{2}\right\rfloor))(因此ans=f(⌊2m+1⌋))
∴g(k)=∑i=km(−1)i−kCikf(i)\therefore g(k)=\sum_{i=k}^{m}(-1)^{i-k}C_{i}^{k}f(i)∴g(k)=∑i=km(−1)i−kCikf(i)
记w=⌊m+12⌋w=\left\lfloor\frac{m+1}{2}\right\rfloorw=⌊2m+1⌋,
ans=∑k=wmg(k)=∑k=wm∑i=km(−1)i−kCikf(i)=∑i=wmf(i)∑k=wi(−1)i−kCikans=\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=wm∑i=km(−1)i−kCikf(i)=∑i=wmf(i)∑k=wi(−1)i−kCik
事实上,优化到这里已经能AC了
但这个式子还可以再优化:
引理1:∑i=wn(−1)n−iCni=(−1)n−wCn−1w−1\color{Red}\sum_{i=w}^{n}(-1)^{n-i}C_{n}^{i}=(-1)^{n-w}C_{n-1}^{w-1}∑i=wn(−1)n−iCni=(−1)n−wCn−1w−1
证明1:
引理2:Cni=Cn−1i−1+Cn−1iC_{n}^{i}=C_{n-1}^{i-1}+C_{n-1}^{i}Cni=Cn−1i−1+Cn−1i
证明2:显然
由引理2,Cn−1i−1=Cni−Cn−1iC_{n-1}^{i-1}=C_{n}^{i}-C_{n-1}^{i}Cn−1i−1=Cni−Cn−1i
∵Cni+1=Cn−1i+Cn−1i+1\because C_{n}^{i+1}=C_{n-1}^{i}+C_{n-1}^{i+1}∵Cni+1=Cn−1i+Cn−1i+1
∴Cn−1i=Cni+1−Cn−1i+1\therefore C_{n-1}^{i}=C_{n}^{i+1}-C_{n-1}^{i+1}∴Cn−1i=Cni+1−Cn−1i+1
∴Cn−1i−1=Cni−(Cni+1−Cn−1i+1)=Cni−Cni+1+Cn−1i+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}∴Cn−1i−1=Cni−(Cni+1−Cn−1i+1)=Cni−Cni+1+Cn−1i+1
设pi−1=Cn−1i−1p_{i-1}=C_{n-1}^{i-1}pi−1=Cn−1i−1,那么
pi−1=Cni−Cni+1+Cn−1i+1=Cni−Cni+1+pi+1p_{i-1}=C_{n}^{i}-C_{n}^{i+1}+C_{n-1}^{i+1}=C_{n}^{i}-C_{n}^{i+1}+p_{i+1}pi−1=Cni−Cni+1+Cn−1i+1=Cni−Cni+1+pi+1
∵pi+1=Cni+2−Cni+3+pi+3\because p_{i+1}=C_{n}^{i+2}-C_{n}^{i+3}+p_{i+3}∵pi+1=Cni+2−Cni+3+pi+3
∴pi−1=Cni−Cni+1+Cni+2−Cni+3+pi+3=Cni−Cni+1+Cni+2−Cni+3+Cni+4−Cni+5+...Cnn\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}∴pi−1=Cni−Cni+1+Cni+2−Cni+3+pi+3=Cni−Cni+1+Cni+2−Cni+3+Cni+4−Cni+5+...Cnn
∴∑i=wn(−1)n−iCni=(−1)n−wpw−1=(−1)n−wCn−1w−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)n−iCni=(−1)n−wpw−1=(−1)n−wCn−1w−1
证毕
∴ans=∑i=wmf(i)∑k=wi(−1)i−kCik=∑i=wm(−1)i−wCi−1w−1f(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)i−kCik=∑i=wm(−1)i−wCi−1w−1f(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;
}