Educational Codeforces Round 86 E. Placing Rooks

首先要满足所有格子都能被攻击,棋子的放置一定是①每一行都有或者每一列都有

(1)当 k = 0 k=0 的时候,同时上面 2 2 个,答案就是 n ! n! ,因为对于第一行有 n n 个位置可放,第二行有 n 1 n-1 个位置号可放…答案就是 n ! n!

(2)当k>=n的时候,答案肯定是0,因为 k k 最多放置的情况就是全部放一列或者一行。

(3)我们先只看把棋子放到每一行,对于列操作类似。

因为要满足k个相对视,所以必然会有n-k列空着,那么我们先选择n-k列出来放置,就是 C n n k C_n^{n-k} ,

然后这n-k列已经选出来了,我们要把这n个不同的棋子放到n-k列里面(棋子不同的原因是因为所在的行不同),

然后问题就转化为把n个不同的球放到 m ( n k ) m(n-k) 个不同的盒子里.

记做 P ( n , m ) P(n,m) ,, P ( n , m ) = S 2 ( n , m ) m ! P(n,m) = S_2(n,m)*m! S 2 S 2 ( n , m ) S_2是第二类斯特林数,S_2(n,m) 含义就是把n个不同球分到m个相同的盒子,然后

这里因为盒子不同,所以需要乘上 m ! m!

所以最终的答案就是 a n s = 2 C n n k P ( n , n k ) ans = 2 * C_n^{n-k}*P(n,n-k) (因为要加上列的情况,所以是2倍),但由于这里 n < = 1 0 5 n<=10^5 ,$n^2求

S_2 T T , 会T_T,不过似乎可以用 nlogn 的方式求 S_2$(菜鸡不会)

题解里面给的就是用容斥来求的 P ( n , m ) = i = 0 m ( 1 ) i C m m i ( m i ) n P(n,m)=\sum_{i=0}^{m}*(-1)^i*C_m^{m-i}*(m-i)^n

我们来粗略证明一下:

这题要求的是不允许有空行(空盒子),所以我们有一个想法:用至少有0个空盒子-至少有1个空盒子

现在我们来看一下至少有 i i 个空盒子的用 C m m i ( m i ) n C_m^{m-i}*(m-i)^n 求出来与实际上的情况的区别。

I : I: 对于至少有0个空盒子,每种情况都只算了一次( C 0 0 C_0^0 )。

I I : II: 对于至少有1个空盒子,就有一部分被重复算了,

实际有2个空盒子的会被算2次。看下图:
在这里插入图片描述
那么如果直接用至少0个空盒子的减去至少1个空盒子的必然多减去一部分,那么就要加上多减去的一部分。

现在来看一下实际有4个空盒子在至少0,1,2,3,4,个空盒子情况下分别被算了多少次。
在这里插入图片描述
这样一来直接把4个空盒子的情况给减去了(因为相加为0了)

对于其他实际k个空盒子也是一样的道理,这样一轮下来都等于0(二项式某个定理:奇数项和=偶数项和)

所以 P ( n , m ) = i = 0 m ( 1 ) i C m m i ( m i ) n P(n,m)=\sum_{i=0}^{m}*(-1)^i*C_m^{m-i}*(m-i)^n 把所有空盒子的情况都给去掉了。

以上纯属自己理解,如有错误,还请指出。

最后代码就好写了

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
typedef long long ll;
const ll mod = 998244353;

ll quick_(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b&1)ans = ans * a % mod;
        a = a * a % mod ;
        b >>= 1;
    }
    return ans;
}



ll fac[man];
void init(int n){
    fac[0] = 1;
    for(int i = 1;i <= n;i++){
        fac[i] = i * fac[i-1] % mod;
    }
}
ll C(ll n,ll m){
    return fac[n] * quick_(fac[m],mod-2) % mod * quick_(fac[n-m],mod-2) % mod;   
}

int main() {
    #ifndef ONLINE_JUDGE
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    int n,k;
    cin >> n >> k;
    if(k>n){
        cout << 0 << endl;
    }else if(k==0){
        ll ans = 1;
        for(int i = 1;i <= n;i++){
            ans = ans * i % mod;
        }
        cout << ans << endl;
    }else{
        ll m = n - k;
        init(n);
        ll ans = C(n,m),sum = 0;
        for(int i = 0;i < m;i++){
            sum = (sum + (i&1 ? -1 : 1)*C(m,m-i)*quick_(m-i,n)%mod + mod)%mod;
        }
        ans = 2 * ans * sum % mod;
        cout << ans << endl;
    }
    return 0;
}
原创文章 93 获赞 12 访问量 8972

猜你喜欢

转载自blog.csdn.net/weixin_43571920/article/details/105890612