zoukankan      html  css  js  c++  java
  • UVAlive-7040 color(组合数学,二项式反演)

    链接:vjudge

    题目大意:有一排方格共 $n$ 个,现在有 $m$ 种颜色,要给这些方格染色,要求相邻两个格子的颜色不能相同。现在问恰好用了 $k$ 种颜色的合法方案数。答案对 $10^9+7$ 取模。$T$ 组数据。

    $1le Tle 300,1le n,mle 10^9,1le kle 10^6,kle min(n,m)$。大多数数据中 $k$ 很小。(smg啊……)


    经典的二项式反演例题。

    我们令 $f(x)$ 为一共有 $x$ 种颜色,恰好用了 $x$ 种颜色的方案数。

    答案就是 ${mchoose k}f(k)$。因为任意选 $k$ 种颜色方案数是一样的。

    这……似乎不太好算?

    我们再令 $g(x)$ 为一共有 $x$ 种颜色,用了至多 $x$ 种颜色的方案数。

    这个就不难算了。第一个格子可以随便填,就是 $x$ 种。后面的格子只要不和上一个颜色相同就行了,就是 $x-1$ 种。

    乘法原理一下:$g(x)=x(x-1)^{n-1}$。$x=0$ 时这个式子是 $0$。

    但是要注意,$x=n=1$ 时我们这样计算是 $0$,但是实际上是 $1$。为什么?$1 imes 0^0$。所以我们要把 $0^0$ 看做 $1$,或者直接特判掉。

    (但是不特判也能过,数据太水,这多组数据没用吧)

    我们来想一想 $f$ 和 $g$ 有什么关系。很容易发现:$g(x)=sumlimits^x_{i=0}{xchoose i}f(i)$。因为 $x$ 种颜色中随便选 $i$ 种都可以。

    标准二项式反演形式。$f(x)=sumlimits^x_{i=0}(-1)^{x-i}{xchoose i}g(i)$。

     因为 $xle 10^6$,所以阶乘和逆元都可以预处理,组合数就可以 $O(1)$ 了。此时 $f(x)$ 就可以 $O(xlog n)$ 算了。

    现在问题就是算 $mchoose k$ 了。$m$ 达到了惊人的 $10^9$,模数又是个大数……怎么办?

    我们发现 $mchoose k$ 可以表示成一种不常用的形式:$frac{m(m-1)(m-2)...(m-k+1)}{k!}$。

    此时分母是预处理过的,分子可以 $O(k)$ 算。这就完事了。

    总时间复杂度 $O(Tklog n)$。因为大多数数据中 $k$ 很小,所以可以跑过。

    ……这数据范围我给满分……

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1000100,mod=1000000007;
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline int read(){
        char ch=getchar();int x=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int t,n,m,k,fac[maxn],inv[maxn],invfac[maxn];
    void init(){    //预处理阶乘,逆元,阶乘的逆元
        fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
        FOR(i,2,1000000){
            fac[i]=1ll*fac[i-1]*i%mod;
            inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
            invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
        }
    }
    int C(int n,int m){
        if(n<=1000000) return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;    //n,m很小,可以直接算
        int ans=invfac[m];    //分母是m的阶乘
        ROF(i,n,n-m+1) ans=1ll*ans*i%mod;    //暴力乘上分子
        return ans;
    }
    int qpow(int a,int b){    //快速幂
        int ans=1;
        for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
        return ans;
    }
    int g(int x){
        if(x==1 && n==1) return 1;    //特判掉x=n=1
        return 1ll*x*qpow(x-1,n-1)%mod;
    }
    int f(int x){
        int ans=0;
        FOR(i,0,x){
            int v=1ll*C(x,i)*g(i)%mod;
            if((x-i)&1) ans=(ans-v+mod)%mod;    //(-1)^(x-i)
            else ans=(ans+v)%mod;
        }
        return ans;
    }
    int main(){
        init();
        t=read();
        FOR(tt,1,t){
            n=read();m=read();k=read();
            printf("Case #%d: %d
    ",tt,int(1ll*C(m,k)*f(k)%mod));    //记得乘上C(m,k)
        }
    }
    二项式反演
  • 相关阅读:
    【转】VS2010中 C++创建DLL图解
    [转]error: 'retainCount' is unavailable: not available in automatic reference counting mode
    [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
    【转】 Tomcat v7.0 Server at localhost was unable to start within 45
    【转】Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If
    【转】SVN管理多个项目版本库
    【转】eclipse安装SVN插件的两种方法
    【转】MYSQL启用日志,和查看日志
    【转】Repository has not been enabled to accept revision propchanges
    【转】SVN库的迁移
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10353695.html
Copyright © 2011-2022 走看看