Placing Rooks | CF 1342E 一个
n
×
n
n\times n
n×n 的棋盘放
n
n
n 个车,攻击范围为十字 让每一个格子都能被打倒,且有
k
k
k 对车之间可以相互攻击到 求方案数
1
≤
n
≤
200000
1\le n\le 200000
1≤n≤200000
1
≤
k
≤
n
(
n
−
1
)
2
1\le k\le \frac{n(n-1)}{2}
1≤k≤2n(n−1)
思路
因为是
n
×
n
n\times n
n×n 的棋盘且
n
n
n 个车且每个格子都要被攻击到 有两种方案,一种每一行都有一个车,一种每一列都有一个车 证明:假设有一行都为空,那么每一列必须至少有一个车让这一行被攻击到,反之也是。 只有当
k
≠
0
k\ne0
k=0 时这两种方案是不同的。 此时方案数需
×
2
\times2
×2 所以我们暂时先考虑
k
>
0
k>0
k>0 的情况
假设每一行都放一个车。 因为有
k
k
k 对车之间可以相互攻击到,那肯定是对于某些列有多个车 假设我们
n
n
n 个车都放在第一列,那么就有
n
−
1
n-1
n−1 对车能相互攻击 但是我们的
k
<
n
−
1
k<n-1
k<n−1,我们就拿第一列的某辆车,挪到那行的另一没有车的列,就能让互相攻击的对数
−
1
-1
−1 也就是说,我们有
k
k
k 对相互攻击的车,意味着就有
k
k
k 列是空着的,没有车
首先我们需要选出
k
k
k 列,让他们空着,方案数就是
C
n
k
C_n^k
Cnk 接下来的问题就是把
n
n
n 个棋子放进这
n
−
k
n-k
n−k 列,且每一列都至少有一个棋子,求方案数了 这个就是一个容斥 的问题了 全集,就是随便放,就是
(
n
−
k
)
n
(n-k)^n
(n−k)n 或者是空一列,就是
(
n
−
k
−
1
)
n
C
n
−
k
1
(n-k-1)^n C_{n-k}^1
(n−k−1)nCn−k1 或者是空两列,就是
(
n
−
k
−
2
)
n
C
n
−
k
2
(n-k-2)^n C_{n-k}^2
(n−k−2)nCn−k2
⋯
\cdots
⋯ 然后对于空奇数列,我们减去;对于空偶数列,我们相加,最后的答案就是:
∑
i
=
0
n
−
k
(
−
1
)
i
(
n
−
k
−
i
)
n
C
n
−
k
i
\sum_{i=0}^{n-k}(-1)^i(n-k-i)^nC_{n-k}^i
i=0∑n−k(−1)i(n−k−i)nCn−ki
其实本题抽象化,就是
n
n
n 个不同小球放到
m
m
m 个不同盒子,要求每个盒子都非空的方案数 可以看到,若预处理后,我们时间复杂度为
O
(
(
n
−
k
)
log
n
)
O((n-k)\log n)
O((n−k)logn) 的
k
k
k 其实最大就是取到
n
−
1
n-1
n−1,不然就是非法情况
代码
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =2e5+50;constint MOD =998244353;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;
ll qpow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res =1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return0;}return res;}
ll inv(ll a){/* */returnqpow(a,MOD-2);}
ll inv(ll a,ll p){returnqpow(a,p-2,p);}
ll fac[MAX];
ll ivfac[MAX];voidinit(int n){
fac[0]=1;for(int i =1;i <= n;++i)fac[i]= fac[i-1]* i % MOD;
ivfac[n]=inv(fac[n]);for(int i = n -1;~i;--i)ivfac[i]= ivfac[i+1]*(i+1)% MOD;}
ll C(int n,int m){if(m <0|| m > n)return0;return fac[n]* ivfac[m]% MOD * ivfac[n - m]% MOD;}intmain(){init(200000);int n;ll k;cin >> n >> k;
ll ans =0;for(int i =0;i <= n - k;++i){
ll pm =(i&1)?-1:1;
ans =(ans + pm *qpow(n-k-i,n)% MOD *C(n-k,i)% MOD + MOD)% MOD;}
ans = ans *C(n,k)% MOD;if(k)ans = ans *2% MOD;
cout << ans;return0;}