欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

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

发布时间:2023/12/3 56 豆豆
生活随笔 收集整理的这篇文章主要介绍了 [XSY] 简单的博弈题(博弈+dp+组合数+容斥) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

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

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

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,jfi1,j1×(p(j1))
(其中ppp表示对手的数字中有ppp个小于a[i]a[i]a[i]
若给第iii个数匹配比他大的数字:
fi,j←fi−1,j×qf_{i,j}\leftarrow f_{i-1,j}\times qfi,jfi1,j×q
(其中qqq表示此时对手的数字中还剩qqq个比a[i]a[i]a[i]大的未匹配)

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

对此,我们的解决方法是
若不给第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=fi1,j+fi1,j1×(p(j1))
在考虑完自己的所有数后,我们再给还没匹配的数统一匹配:
fm,j=fm,j×(m−j)!f_{m,j}=f_{m,j}\times(m-j)!fm,j=fm,j×(mj)!
但这样我们就不能保证所有数中恰好\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)ikCikf(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=wmi=km(1)ikCikf(i)=i=wmf(i)k=wi(1)ikCik
事实上,优化到这里已经能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)niCni=(1)nwCn1w1
证明1:

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

由引理2,Cn−1i−1=Cni−Cn−1iC_{n-1}^{i-1}=C_{n}^{i}-C_{n-1}^{i}Cn1i1=CniCn1i
∵Cni+1=Cn−1i+Cn−1i+1\because C_{n}^{i+1}=C_{n-1}^{i}+C_{n-1}^{i+1}Cni+1=Cn1i+Cn1i+1
∴Cn−1i=Cni+1−Cn−1i+1\therefore C_{n-1}^{i}=C_{n}^{i+1}-C_{n-1}^{i+1}Cn1i=Cni+1Cn1i+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}Cn1i1=Cni(Cni+1Cn1i+1)=CniCni+1+Cn1i+1
pi−1=Cn−1i−1p_{i-1}=C_{n-1}^{i-1}pi1=Cn1i1,那么
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}pi1=CniCni+1+Cn1i+1=CniCni+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+2Cni+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}pi1=CniCni+1+Cni+2Cni+3+pi+3=CniCni+1+Cni+2Cni+3+Cni+4Cni+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)niCni=(1)nwpw1=(1)nwCn1w1
证毕

∴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)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; }

总结

以上是生活随笔为你收集整理的[XSY] 简单的博弈题(博弈+dp+组合数+容斥)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。