E. Placing Rooks(第二类斯特林数 容斥原理)

http://codeforces.com/contest/1342/problem/E

题意:

给n*n棋盘,要放n个棋。

每个棋的攻击范围为所在行和列。要求所有位置都至少被一个棋子攻击。要求有k对棋子两两对视。

解析:

假设k=0,那么就是 n ! n! n!

否则 k ∈ [ 1 , n − 1 ] k\in[1,n-1] k[1,n1],情况是n个棋子占据 n − k n-k nk个行或者 n − k n-k nk个列。

那么方案数是 2 ⋅ C ( n , n − k ) ⋅ F ( n , n − k ) 2\cdot C(n,n-k)\cdot F(n,n-k) 2C(n,nk)F(n,nk)。( F ( n , m ) F(n,m) F(n,m)表示将n个不同的球分到m个不同的盒子的方案数)

F ( n , m ) = m ! S 2 ( n , m ) F(n,m)=m!S_2(n,m) F(n,m)=m!S2(n,m),但是我们这里只需要求一个而已。


考虑容斥,至少 i i i个盒子为空的方案数为 C ( m , m − i ) ⋅ ( m − i ) n C(m,m-i)\cdot (m-i)^n C(m,mi)(mi)n,对于这些容斥即可。

F ( n , m ) = ∑ i = 0 m ( − 1 ) i C ( m , m − i ) ⋅ ( m − i ) n F(n,m)=\sum_{i=0}^m(-1)^{i}C(m,m-i)\cdot (m-i)^n F(n,m)=i=0m(1)iC(m,mi)(mi)n

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-04-29-10.30.11
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=998244353 ;
const int maxn=2e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
LL Pow(LL a,LL b,LL mod){
    if(a>=mod)a%=mod;
    LL res=1;
    while(b>0){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
/*_________________________________________________________Pow*/

LL fac[maxn];
LL ifac[maxn];
LL C(LL n,LL m){return fac[n]*ifac[m]%mod*ifac[n-m]%mod; }
LL deal(LL n,LL m){
    LL ans=0;
    LL f=1;
    rep(i,0,m){
        ans+=f*C(m,m-i)*Pow(m-i,n,mod)%mod;
        ans=(ans%mod+mod)%mod;
        f=-f;
    }
    return ans;
}
int main(){
    fac[0]=1;
    rep(i,1,maxn-1)fac[i]=fac[i-1]*i%mod;
    ifac[maxn-1]=Pow(fac[maxn-1],mod-2,mod);
    per(i,maxn-2,0)ifac[i]=ifac[i+1]*(i+1)%mod;
    LL n=rd,k=rd;
    if(k>=n)return 0*printf("%d\n",0);
    if(k==0)return 0*printf("%lld\n",fac[n]);
    printf("%lld\n",2ll*C(n,n-k)*deal(n,n-k)%mod);
    return 0;
}

/*_________________________________________________________end*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值