HAOI2018染色——容斥

题目大意

loj

思路

设\(f_i\)表示至少出现了i种颜色的方案数

\[
\begin{aligned}
f_i&={m \choose i}\times \frac{(s\times i)!}{(s!)^{i}}\times {n\choose s\times i}\times (m-i)^{n-s\times i}\f_i&={m \choose i}\times \frac{n!}{(s!)^{i}\times (n-s\times i)!}\times (m-i)^{n-s\times i}\\end{aligned}
\]

设\(g_i\)表示恰好出现了i种颜色的方案数,不难得到\(g_i\)的容斥式子。

\[
\begin{aligned}
g_i=&f_i-\sum_{j=i+1}^{m}g_{j}\times {j\choose i}\g_i=&f_i-\frac{\sum_{j=i+1}^{m}\times \frac{g_j\times j!}{(j-i)!}}{i!}
\end{aligned}
\]

上面是一个经典的分治FFT形式的式子,使用分治FFT可以做到\(n\log^2n\)。

其实可以更加优化,上面的式子同样也是一个经典的二项式反演。

\[
\begin{aligned}
f_i&=\sum_{j=i}^{m}{j\choose i}\times g_{j}\g_i&=\sum_{j=i}^{m}(-1)^{j-i}\times {j\choose i}\times f_{j}
\end{aligned}
\]

直接用NTT一遍求出g的值即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.3.22
 * Problem : loj2527_ntt
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("loj2527_ntt.in","r",stdin);
    freopen("loj2527_ntt.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=1e7+10;
const int maxm=1e5+10;
const int mod=1004535809;
int n,m,S,w[maxm],ans;
int fac[maxn],ifac[maxn];

int qpow(int x,int y=mod-2){
    int ret=1; x%=mod;
    while(y){
        if(y&1)ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ret;
}

void inc(int &x,int y){
    x+=y;
    if(x>=mod)x-=mod;
    else if(x<0)x+=mod;
}

int C(int x,int y){
    if(x<0 || y<0 || x<y)return 0;
    return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}

namespace Poly{
    int om[maxm<<2],dn[maxm<<2],lim,cnt;
    void dft(int *A,int ty){
        if(ty==-1)reverse(A+1,A+lim);
        REP(i,0,lim-1)if(i<dn[i])swap(A[i],A[dn[i]]);
        for(int len=1;len<lim;len<<=1){
            int w=om[len<<1];
            for(int L=0;L<lim;L+=len<<1){
                int wk=1;
                REP(i,L,L+len-1){
                    int u=A[i],v=1ll*A[i+len]*wk%mod;
                    A[i]=(u+v)%mod;
                    A[i+len]=(u-v)%mod;
                    wk=1ll*wk*w%mod;
                }
            }
        }
        if(ty==-1){
            int inv=qpow(lim);
            REP(i,0,lim-1)A[i]=(1ll*A[i]*inv%mod+mod)%mod;
        }
    }
}

void init(){
    read(n),read(m),read(S);
    REP(i,0,m)read(w[i]);
    fac[0]=1;
    int lim=max(n,m);
    REP(i,1,lim)fac[i]=1ll*fac[i-1]*i%mod;
    ifac[lim]=qpow(fac[lim]);
    DREP(i,lim-1,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}

int f[maxm<<2],g[maxm<<2];

using namespace Poly;

int main(){
//  File();
    init();
    REP(i,0,m)if(S*i<=n)f[i]=1ll*C(m,i)*fac[S*i]%mod*
        qpow(ifac[S],i)%mod*C(n,S*i)%mod*qpow(m-i,n-S*i)%mod*fac[i]%mod;
    REP(i,0,m)g[i]=1ll*(i&1 ? -1 : 1)*ifac[i]%mod;
    reverse(g,g+m+1);

    lim=1,cnt=0;
    while(lim<=m+m)lim<<=1,++cnt;
    if(!cnt)cnt=1;
    om[lim]=qpow(3,(mod-1)/lim);
    for(int i=lim>>1;i;i>>=1)
        om[i]=1ll*om[i<<1]*om[i<<1]%mod;
    REP(i,0,lim-1)dn[i]=dn[i>>1]>>1|((i&1)<<(cnt-1));

    dft(f,1),dft(g,1);
    REP(i,0,lim-1)g[i]=1ll*g[i]*f[i]%mod;
    dft(g,-1);

    REP(i,0,m)inc(ans,1ll*g[i+m]*w[i]%mod*ifac[i]%mod);
    printf("%d\n",ans);

    return 0;
}
/*=======================================
 * Author : ylsoi
 * Time : 2019.3.21
 * Problem : loj2527
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("loj2527.in","r",stdin);
    freopen("loj2527.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=1e7+10;
const int maxm=1e5+10;
const int mod=1004535809;
int n,m,S,w[maxm],ans;
int fac[maxn],ifac[maxn];

int qpow(int x,int y=mod-2){
    int ret=1; x%=mod;
    while(y){
        if(y&1)ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ret;
}

int C(int x,int y){
    if(x<0 || y<0 || x<y)return 0;
    return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}

void inc(int &x,int y){
    x+=y;
    if(x>=mod)x-=mod;
    else if(x<0)x+=mod;
}

namespace Poly{
    int om[maxm<<2],dn[maxm<<2],lim,cnt;
    void dft(int *A,int ty){
        if(ty==-1)reverse(A+1,A+lim);
        REP(i,0,lim-1)if(i<dn[i])swap(A[i],A[dn[i]]);
        for(int len=1;len<lim;len<<=1){
            int w=om[len<<1];
            for(int L=0;L<lim;L+=len<<1){
                int wk=1;
                REP(i,L,L+len-1){
                    int u=A[i],v=1ll*A[i+len]*wk%mod;
                    A[i]=(u+v)%mod;
                    A[i+len]=(u-v)%mod;
                    wk=1ll*wk*w%mod;
                }
            }
        }
        if(ty==-1){
            int inv=qpow(lim);
            REP(i,0,lim-1)A[i]=1ll*A[i]*inv%mod;
        }
    }
    void multi(int *A,int *B,int *C,int la,int lb){
        lim=1,cnt=0;
        while(lim<=la+lb)lim<<=1,++cnt;
        REP(i,0,lim-1){
            dn[i]=dn[i>>1]>>1|((i&1)<<(cnt-1));
            if(i>la)A[i]=0;
            if(i>lb)B[i]=0;
        }
        dft(A,1),dft(B,1);
        REP(i,0,lim-1)C[i]=1ll*A[i]*B[i]%mod;
        dft(C,-1);
    }
}

void math_init(){
    fac[0]=1;
    int lim=max(n,m);
    REP(i,1,lim)fac[i]=1ll*fac[i-1]*i%mod;
    ifac[lim]=qpow(fac[lim],mod-2);
    DREP(i,lim-1,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;

    using namespace Poly;
    lim=1;
    while(lim<=m+m)lim<<=1;
    om[lim]=qpow(3,(mod-1)/lim);
    for(int i=lim>>1;i;i>>=1)
        om[i]=1ll*om[i<<1]*om[i<<1]%mod;
}

int f[maxm<<2],g[maxm<<2],ap[maxm<<2],bp[maxm<<2];

void divide(int l,int r){
    if(l==r){
        g[l]=(f[l]-1ll*g[l]*ifac[l]%mod)%mod;
        inc(ans,1ll*g[l]*w[l]%mod);
        return;
    }

    using namespace Poly;
    int mid=(l+r)>>1;

    divide(mid+1,r);

    REP(i,0,r-mid-1)ap[i]=1ll*g[i+mid+1]*fac[i+mid+1]%mod;
    REP(i,0,r-l-1)bp[i]=ifac[i+1];
    reverse(bp,bp+r-l);
    multi(ap,bp,ap,r-mid-1,r-l-1);
    REP(i,l,mid)inc(g[i],ap[i-mid-1+r-l]);

    divide(l,mid);
}

int main(){
    //File();

    read(n),read(m),read(S);
    REP(i,0,m)read(w[i]);

    math_init();

    REP(i,0,m)if(S*i<=n)
        f[i]=1ll*C(m,i)*fac[n]%mod*qpow(ifac[S],i)%mod*ifac[n-S*i]%mod*qpow(m-i,n-S*i)%mod;

    divide(0,m);

    printf("%d\n",ans);

    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10584158.html

时间: 2024-08-30 14:00:46

HAOI2018染色——容斥的相关文章

HAOI2018 [HAOI2018]染色 【组合数 + 容斥 + NTT】

题目 为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可 以抽象为一个长度为 \(N\) 的序列, 每个位置都可以被染成 \(M\) 种颜色中的某一种. 然而小 C 只关心序列的 \(N\) 个位置中出现次数恰好为 \(S\) 的颜色种数, 如果恰 好出现了 \(S\) 次的颜色有 \(K\) 种, 则小 C 会产生 \(W_k\) 的愉悦度. 小 C 希望知道对于所有可能的染色方案, 他能获得的愉悦度的和对 1004535809 取模的结果是多少. 输入格式 从

4487[Jsoi2015]染色问题 容斥+组合

4487: [Jsoi2015]染色问题 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 211  Solved: 127[Submit][Status][Discuss] Description 棋盘是一个n×m的矩形,分成n行m列共n*m个小方格.现在萌萌和南南有C种不同颜色的颜料,他们希望把棋盘用这些颜料染色,并满足以下规定:1.  棋盘的每一个小方格既可以染色(染成C种颜色中的一种) ,也可以不染色.2.  棋盘的每一行至少有一个小方格被染

HAOI 2018 染色(容斥+NTT)

题意 https://loj.ac/problem/2527 思路 设 \(f(k)\) 为强制选择 \(k\) 个颜色出现 \(s\) 种,其余任取的方案数. 则有 \[ f(k)={m\choose k}{n\choose sk}{(sk)!\over(s!)^k}(m-k)^{n-sk} \] 不难看出,这个方案可能包括了超过 \(k\) 种颜色,也有重复的方案,所以恰有 \(k\) 个颜色出现 \(s\) 种的方案 \(ans_k\) 满足 \[ ans_k=\sum_{i=k}^{\m

【BZOJ2969】矩形粉刷 概率+容斥

[BZOJ2969]矩形粉刷 Description 为了庆祝新的一年到来,小M决定要粉刷一个大木板.大木板实际上是一个W*H的方阵.小M得到了一个神奇的工具,这个工具只需要指定方阵中两个格子,就可以把这两格子为对角的,平行于木板边界的一个子矩形全部刷好.小M乐坏了,于是开始胡乱地使用这个工具. 假设小M每次选的两个格子都是完全随机的(方阵中每个格子被选中的概率是相等的),而且小M使用了K次工具,求木板上被小M粉刷过的格子个数的期望值是多少. Input 第一行是整数K,W,H Output 一

Codeforces 100548F - Color (组合数+容斥)

题目链接:http://codeforces.com/gym/100548/attachments 有n个物品 m种颜色,要求你只用k种颜色,且相邻物品的颜色不能相同,问你有多少种方案. 从m种颜色选k种颜色有C(m, k)种方案,对于k种颜色方案为k*(k-1)^(n-1)种.但是C(m, k)*k*(k-1)^(n-1)方案包括了选k-1,k-2...,2种方案. 题目要求刚好k种颜色,所以这里想到用容斥. 但是要是直接C(m, k)*k*(k-1)^(n-1) - C(m, k-1)*(k

P4491 [HAOI2018]染色

传送门 我觉得自己的数学也是够差的--一点思路也没有-- 考虑容斥,首先\(lim=min(m,n/S)\),设\(f[i]\)表示出现恰好\(S\)次的元素大于等于\(i\)种的情况,我们随便选\(i\)种颜色放\(S\)次,选的方法数有\(C_m^i\)种,然后染色可以看做是一个类似全排列的东西,每连续的几个染上同样的颜色,那么方案数为\(\frac{n!}{(S!)^i(n-S*i)!}\),前面颜色已经选定了,后面的每个有\(m-i\)种颜色可选,所以还要乘上一个\((m-i)^{n-S

[HAOI2018]染色

题目描述 为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可 以抽象为一个长度为 N 的序列, 每个位置都可以被染成 M 种颜色中的某一种. 然而小 C 只关心序列的 N 个位置中出现次数恰好为 S 的颜色种数, 如果恰 好出现了 S 次的颜色有 K 种, 则小 C 会产生 Wk? 的愉悦度. 小 C 希望知道对于所有可能的染色方案, 他能获得的愉悦度的和对 1004535809取模的结果是多少. 题解 碰到这种等于什么什么的题要考虑容斥. 既然是容斥,那么先设c

「总结」容斥。二.反演原理

二.反演原理 0.综述 说一下个人对反演的理解. 反演是一种手段,一种处理已知信息和未知信息关系的手段,用来得到未知信息的方式.也就是以一种既定的手段在较小的时间复杂度内用已知的信息得到未知的信息. 还有$zsq$学长更加浅显的解读. 反演一般就是把一个好看但难算的式子转化成一个难看且难算的式子在转化为一个难看但好算的式子. 先来一个裸一点的反演 下面要说我知道的四种反演. 子集反演,针对的是集合交并的容斥. 二项式反演,针对组合原理的容斥. 莫比乌斯反演,针对约数和倍数的容斥. 斯特林反演,针

【题解】Gnutella Chessmaster(容斥+下降幂+二项式定理)

[题解]Gnutella Chessmaster(容斥+下降幂+二项式定理) Gnutella Chessmaster 绝世好题! 题目大意:有一个\(n\times n\)的棋盘,现在要在上面放\(k\)个Bishop,每个Bishop打两条对角线,问你放Bishop的方案数,方案不同当且仅当一个位置上存在主教的状态不同.你要对于每个k输出方案. prop1 对于这个棋盘二分图染色((1,1)=白),显然白色格子上面的Bishop打不到黑色格子上面的Bishop,于是我们可以分开算黑白格子的方