简单的博弈题
对于贪心的对手,显然用最大的一半和他最小的一半比较判断是否全胜。(这不就是田忌赛马吗)
对于随机的对手,先考虑暴力怎么做:
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,j←fi−1,j−1×(p−(j−1))
(其中
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,j←fi−1,j×q
(其中
q
q
q表示此时对手的数字中还剩
q
q
q个比
a
[
i
]
a[i]
a[i]大的未匹配)
问题来了,要算出 q q q,必须知道前 i − 1 i-1 i−1个数具体匹配了对手的哪些数,那不就跟暴力一样了吗?
对此,我们的解决方法是
若不给第
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=fi−1,j+fi−1,j−1×(p−(j−1))
在考虑完自己的所有数后,我们再给还没匹配的数统一匹配:
f
m
,
j
=
f
m
,
j
×
(
m
−
j
)
!
f_{m,j}=f_{m,j}\times(m-j)!
fm,j=fm,j×(m−j)!
但这样我们就不能保证所有数中
恰
好
\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)i−kCikf(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=wm∑i=km(−1)i−kCikf(i)=∑i=wmf(i)∑k=wi(−1)i−kCik
事实上,优化到这里已经能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)n−iCni=(−1)n−wCn−1w−1
证明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=Cn−1i−1+Cn−1i
证明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}
Cn−1i−1=Cni−Cn−1i
∵
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=Cn−1i+Cn−1i+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}
∴Cn−1i=Cni+1−Cn−1i+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}
∴Cn−1i−1=Cni−(Cni+1−Cn−1i+1)=Cni−Cni+1+Cn−1i+1
设
p
i
−
1
=
C
n
−
1
i
−
1
p_{i-1}=C_{n-1}^{i-1}
pi−1=Cn−1i−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
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}
pi−1=Cni−Cni+1+Cn−1i+1=Cni−Cni+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+2−Cni+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}
∴pi−1=Cni−Cni+1+Cni+2−Cni+3+pi+3=Cni−Cni+1+Cni+2−Cni+3+Cni+4−Cni+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)n−iCni=(−1)n−wpw−1=(−1)n−wCn−1w−1
证毕
∴ 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)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;
}