【经典】容斥+排列组合——cf1342E

/*
设每行都有一个,每列上每多一个攻击对就会+1,
列上多了k个,那么只有n-k列上有棋子C(n,n-k)
问题变成将n个棋子放在n-k列上,且每列必须有一个,每行必须有且仅有一个的方案数
容斥:
    ans=将n个棋子随便放在某一列 pow(n-k,n)
    -有一个空行 pow(n-k-1,n)C(n-k,1) 
     +有两个空行 pow(n-k-2,n)C(n-k,2)
     ...
最后,在n行里面选n-k行的方案数是C(n,n-k)
如果是n列里选n-k列,则方案数还要再*2
所以答案是ans*C(n,n-k)*2 
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mod 998244353
#define N 200006

ll n,k,F[N],inv[N];

ll Pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b%2)res=res*a%mod;
        b>>=1;a=a*a%mod;
    }
    return res;
}
void init(){
    F[0]=inv[0]=1;
    for(int i=1;i<=n;i++){
        F[i]=F[i-1]*i%mod;
        inv[i]=Pow(F[i],mod-2);
    }
}
ll C(ll a,ll b){
    if(a<b)return 0;
    if(a==b || b==0)return 1; 
    return F[a]*inv[b]%mod*inv[a-b]%mod;
}
ll calc(ll i){//有i个空行的情况
    ll res=Pow(n-k-i,n);
    res=res*C(n-k,i)%mod; 
    return res;
}

int main(){
    cin>>n>>k;
    init();
    if(k>=n){puts("0");return 0;}
    else if(k==0){
        cout<<F[n]<<"\n";
    }else {
        ll ans=Pow(n-k,n);
        int sign=1;
        for(int i=1;i<=n-k-1;i++){
            sign*=-1;
            ans=(ans+sign*calc(i)%mod+mod)%mod;
        }
        cout<<ans*C(n,n-k)*2%mod<<'\n';
    }
}

猜你喜欢

转载自www.cnblogs.com/zsben991126/p/12793040.html
今日推荐