现在的位置: 首页 > 综合 > 正文

Codeforces GYM 100548 F – Color 2014-2015 ACM-ICPC, Asia Xian Regional Contest

2018年01月14日 ⁄ 综合 ⁄ 共 3100字 ⁄ 字号 评论关闭

挣扎了好几天终于把这一题过了。。比赛的时候RP低到爆忘记带费马小定理和快速幂的模板,好不容易YY出了一个组合数的求法,然后发现公式推错了,最后呵呵呵呵。><

一开始的想法是C(m,k)k(k-1)^(n-1),但是这种case包括的是颜色数<=k的情形,所以还要减去颜色数<=(k-1)的情形。

C(m,k)[k(k-1)^(n-1)-(k-1)(k-2)^(n-1)]

下面考虑这种case,如果选取的k个颜色是1,2,3,4,那么会有一种染色方案是1,2,1,2...,如果选取的k-1个颜色是1,2,3,那么仍然会包含一种染色方案1,2,1,2...所以上面的公式减多了,还要再加上颜色数<=(k-2)的case,然后再减去颜色数<=(k-3)的case。

考虑有k种颜色时的染色方案:sum (-1)^i*C(k,i)*(k-i)*(k-i-1)^(n-1) for i=0,1,...,k

总共有m种颜色可以选择,所以再乘上C(m,k)即可。

因为涉及到组合数取模,所以需要求逆元,因为只有乘法才可以边乘边取模。由于1e9+7是质数,求逆元可以用用费马小定理,a^(p-1)=1%p
那么a^(p-1)/a =a^(p-2)= 1/a%p ,其中gcd(a,p)=1,p is a prime。最后用快速幂求解即可。

1,2,3....的逆元可以预处理。组合数可以递推求解。

一直WA on test 2 ,最后发现是相乘的过程中少%mod,估计是中途爆long long了。难怪小数据对拍都查不出来==

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<stdlib.h>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<ctype.h>
#include<map>
#include<time.h>
#include<bitset>
using namespace std;
//Xi'an Reigonal F

int T;
long long n;
long long m;
long long k;
const long long mod=1000000007;
const int maxn=1000010;
long long comb[maxn];
long long inv[maxn];
long long ans;
long long pow_mod(long long a,long long b)
{
    long long s=1;
    long long t=1;
    while(b)
    {
        if(b&t)
        {
            s=(s*a)%mod;
        }
        a=(a*a)%mod;
        b=b>>1;
    }
    return s;
}
void cal_inv()
{
    for(int i=1;i<maxn;i++)
    {
        inv[i]=pow_mod(i,mod-2)%mod;
    }
}
void cal_comb(long long m0,long long k0)//C(m,0),C(m,1),C(m,2)....C(m,k)
{
    comb[0]=1;
    for(long long i=1;i<=k0;i++)
    {
        comb[i]=((comb[i-1]*(m0-i+1))%mod)*inv[i]%mod;
        //cout<<i<<" " <<comb[i]<<endl;
    }
}
void solve()
{
    cal_comb(m,k);
    ans=comb[k]%mod;
    memset(comb,0,sizeof(comb));
    long long ret=1;
    cal_comb(k,k);
    long long tmp=0;
//    for(int i=k;i>0;i--) //equivalent to the following one, if we replace i with k-j
//    {
//        tmp+=ret*i*pow_mod(i-1,n-1)%mod*comb[i]%mod;
//        tmp=(tmp+mod)%mod;
//        ret=-ret;
//    }
    for(long long i=0;i<=(k-1);i++)
    {
        tmp+=ret*(k-i)*comb[i]%mod*pow_mod(k-i-1,n-1)%mod;//之前写成了tmp+=ret*comb[i]%mod*(k-i)*pow_mod(k-i-1,n-1);然后一直WA on test 2,估计是中间的相乘爆long long了。
        //tmp+=ret*comb[i]%mod*(k-i)%mod*pow_mod(k-i-1,n-1)%mod; //it‘s also ok.
        tmp=(tmp+mod)%mod;
        ret=-ret;
    }
    ans=(ans*tmp)%mod;
}
int main()
{
    //freopen("input.txt","r",stdin);
    freopen("data.txt","r",stdin);
    freopen("out1.txt","w",stdout);
    scanf("%d",&T);
    memset(inv,0,sizeof(inv));
    cal_inv();
    for(int ca=1;ca<=T;ca++)
    {
        memset(comb,0,sizeof(comb));
        ans=1;
        scanf("%I64d %I64d %I64d",&n,&m,&k);
        if(n==1&&m==1)
        {
            ans=1;
        }
        else
        {
            solve();
        }
        printf("Case #%d: %I64d\n",ca,ans);
    }
    return 0;
}



顺便再附上暴力打表验证的的代码,其实数据大一点基本就跑不出来了==

#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<stdlib.h>
#include<stdio.h>
#include<queue>
#include<cstring>
#include<time.h>
using namespace std;
//Xi'an Reigonal F BF
const int maxn=1000010;
int T;
int n;
int m;
int k;
long long ans;
int s[maxn];
int used[maxn];
void dfs(int dep)
{
    if(dep==n)
    {
        memset(used,0,sizeof(used));
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            int t=s[i];
            if(used[t]==0)
            {
                cnt++;
                used[t]=1;
            }
        }
        if(cnt==k)
        {
            ans++;
            for(int i=1;i<=n;i++)
            {
                printf("%d ",s[i]);
            }
            puts("");
        }
        return;
    }
    for(int i=1;i<=m;i++)
    {
        if(s[dep]!=i)
        {
            s[dep+1]=i;
            dfs(dep+1);
            s[dep+1]=0;
        }
    }

}
int main()
{
    //freopen("input.txt","r",stdin);
    freopen("data.txt","r",stdin);
    freopen("out2.txt","w",stdout);
    scanf("%d",&T);
    for(int ca=1;ca<=T;ca++)
    {
        scanf("%d %d %d",&n,&m,&k);
        ans=0;
        memset(s,0,sizeof(s));
        dfs(0);
        printf("Case #%d: %I64d\n",ca,ans);
    }

	return 0;
}


抱歉!评论已关闭.