UVA 7040 Color 组合数加容斥原理

点击打开链接


题意:告诉n个人排成一排,然后有m种颜色,要给这n个人正好涂k种颜色,相邻两个人颜色要不同,问有多少种方法。

分析:m种颜色选k种,所以有C(m,k),对于选出的k种颜色去给n个人涂色,对于第一个人有k种,第二个人有k-1种,第三个人k-1种。。。。。所以总共是k*(k-1)^(n-1)种。然而不要以为这样就好了,因为这样算并不能保证是一定用了k种颜色的,可能只是选了k种其中的2,3,4,,,,k-1种,所以要用容斥原理来去重,所以最后的式子就是

C(m,k) × ( k × (k-1)^(n-1) + ((-1)^p × C(k, p) × p × (p-1)^(n-1) ) (2 <= p <= k-1)


#include <iostream>
#include <stdio.h>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
typedef long long ll;
const ll mod=1e9+7;
using namespace std;

ll in[1000100],cm[1000100],ck[1000100];

ll fun(ll m,ll n)
{
    ll b=1;
    while(n)
    {
        if(n&1)
            b=b*m%mod;
        n>>=1;
        m=m*m%mod;
    }
    return b;
}

void init()//打表求逆元
{
    in[1]=1;
    for(ll i=2;i<=1000009;i++)
        in[i]=(mod-mod/i)*in[mod%i]%mod;
}

ll n,m,k;

void fff()
{
    cm[0]=ck[0]=1;

    for(ll i=1;i<=k;i++)
    {
        cm[i]=cm[i-1]%mod*(m-i+1)%mod*in[i]%mod;
        ck[i]=ck[i-1]%mod*(k-i+1)%mod*in[i]%mod;
    }
}

int main()
{
    int T;
    init();

    scanf("%d",&T);
    for(int ca=1;ca<=T;ca++)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        fff();//打表求组合数
        printf("Case #%d: ",ca);

        int tmp=1;
        ll ans=0;

        for(ll i=k;i>=1;i--)
        {
            ans=(ans+tmp*i*ck[i]%mod*fun(i-1,n-1)%mod+mod)%mod;
            tmp=-tmp;
        }
        ans=ans*cm[k]%mod;

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

    }
    return 0;
}


你可能感兴趣的:(UVA 7040 Color 组合数加容斥原理)