题面
题解
对于贪心的对手的情况,田忌赛马即可。
对于随机的对手,发现使用任何策略都不影响结果。那我们只需要选一种自己数字的排列并固定下来,再去和对手的数字的 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]=1ifi≥1dp[i][j]=dp[i−1][j]+dp[i−1][j−1]×(si−(j−1))ifi,j≥1
然后设 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]=(m−j)!×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=w∑m(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=w∑m(−1)i−w(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+1∑mg[w]w=2m+1∑mi=w∑m(−1)i−w(wi)f[i]i=2m+1∑mf[i]w=2m+1∑i(−1)i−w(wi)i=2m+1∑mf[i](2m−1i−1)
其中最后一步用到了引理 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=i∑n(−1)j−i(jn)=(i−1n−1)
引理 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)=(in−1)+(i−1n−1)
引理 2 证明:
公式化的理解:手动拆开式子然后约分。
从组合数上的意义理解:从 n n n 个物品中挑选出 i i i 个物品,相当于考虑是否最终挑选出第 i i i 个物品,如果不挑选出,那么方案数为 ( n − 1 i ) \dbinom{n-1}{i} (in−1),如果挑选出,那么方案为 ( n − 1 i − 1 ) \dbinom{n-1}{i-1} (i−1n−1)。
那么:
( 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} ===(i−1n−1)(in)−(in−1)(in)−((i+1n)−(i+1n−1))(in)−(i+1n)+(i+1n−1)
令 p i − 1 = ( n − 1 i − 1 ) p_{i-1}=\dbinom{n-1}{i-1} pi−1=(i−1n−1),那么 p i − 1 = ( n i ) − ( n i + 1 ) + p i + 1 p_{i-1}=\dbinom{n}{i}-\dbinom{n}{i+1}+p_{i+1} pi−1=(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 pi−1=(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} (i−1n−1)=pi−1=j=i∑n(−1)j−i(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
*/