zoukankan      html  css  js  c++  java
  • 【经典】容斥+排列组合——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]<<"
    ";
        }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<<'
    ';
        }
    }
  • 相关阅读:
    嵌入式Linux基础知识
    面向对象程序设计与原则
    设计模式之工厂模式
    设计模式之单例模式
    基于Doxygen的C/C++注释原则
    设计模式概论
    重启博客
    java 单例模式的几种写法
    CountDownLatch、CyclicBarrier
    java ConcurrentHashMap和CopyOnWriteArrayList解决并发问题
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12793040.html
Copyright © 2011-2022 走看看