壹、题目描述 ¶
贰、题解 ¶
显然,这是要单独考虑每个城市对于答案的贡献。
对于每个城市,我们可以考虑它被覆盖的情况有多少种,但是尝试之后不难发现,这样计算太复杂了 —— 一个城市可以只被一个点覆盖,也可能被两个,三个......
考虑容斥?容斥什么?怎么容斥?时间复杂度再怎么也要个 \(2^n\) 吧?复杂度 \(\mathcal O(m2^n)\)?Soshite?
显然,这个思路不可取,但是我们总不可能离开 “方案” 吧?但是我们可以考虑正难则反。
对于一个城市,总的方案数有 \(n!\) 种,我们只需要计算出不能覆盖它的点选择情况就好了。
显然,在第 \(i\) 次选择点的时候,选择点的距离必须要严格大于 \(n-i+1\),所以,我们记 \(cnt(i,j)\) 表示到达城市 \(i\) 的点中,距离严格大于 \(j\) 的有多少。
那么对于一个点,它不被覆盖的方案数即
\[\prod_{i=1}^{n}(cnt(i,n-j+1)-j+1) \]为什么后面还要 \(-j+1\)?因为在前面 \([1,i-1]\) 次选择种,已经选了一些点,而我们不能重复选择这些已经被选择的点,所以得减去。
但是注意特殊情况,当出现 \(cnt(i,n-j+1)-j+1\le 0\) 时,说明这个点无论如何都会被选择,直接让方案数为 \(0\) 即可。
叁、参考代码 ¶
const int maxn=20;
const int maxm=5e4;
const int mod=998244353;
inline int qkpow(int a, int n){
int ret=1;
for(; n; n>>=1, a=1ll*a*a%mod)
if(n&1) ret=1ll*ret*a%mod;
return ret;
}
int d[maxn+5][maxm+5];
int n, m;
int cnt[maxm+5][maxn+5];
inline void input(){
n=readin(1), m=readin(1);
rep(i, 1, n) rep(j, 1, m)
d[i][j]=readin(1);
}
signed main(){
input();
rep(i, 1, m) rep(j, 1, n) rep(k, 1, n) if(d[k][i]>j)
++cnt[i][j];
int ans=0, fac=1;
rep(i, 1, n) fac=1ll*fac*i%mod;
rep(i, 1, m){
int cur=1;
rep(j, 1, n){
if(cnt[i][n-j+1]-j+1<=0){ cur=0; break; }
cur=1ll*cur*(cnt[i][n-j+1]-j+1)%mod;
}
ans=(0ll+ans+fac+mod-cur)%mod;
}
ans=1ll*ans*qkpow(fac, mod-2)%mod;
printf("%d\n", ans);
return 0;
}
肆、用到 の Trick ¶
正着算方案时,如果遇到困难,何不试试正难则反?