HDU6038 Function

題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=6038


【題意】存在一個函數f將一個0-(n-1)的集合映射到一個0-(m-1)的集合。存在兩個數組A,B,已知函數滿足f(i)=b_f(a_i)。

給出A,B的具體數值,求存在多少種合法的函數f滿足條件


【分析】簡單嘗試可以發現函數f的限制條件需要利用A數組的相互對應關係,而A,B兩數組數值兩兩不相同,其值和位置構成的對應鏈必然成環(單點認爲是自環,屬於合法情況),如此等式的形成需要利用A的環映射到B的環,故可視爲將b環拆開爲一條鏈,一條和多條鏈拼接後重組的環的長度爲A環的長度。於是題目就轉換成求A數組中環大小和對應的數目,B數組中環大小和對應的數目。枚舉A中環的大小,再對所有因子進行枚舉,統一個環的映射數爲加分,環與環之間用乘法,最後獲得答案,複雜的爲O(n^(3/2))。比賽爲了方便,代碼比較醜,採用並查集來統計環的大小


【代碼】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define eps 1e-8
#define PI acos(-1.0)
const int mod=1e9+7;
int a[100100];
int b[100100];
int fa[100100];
int fb[100100];
int numa[100100];
int numb[100100];
int suma[100100];
int sumb[100100];
LL quick_pow(LL a,LL b){
    LL ret=1;
    while(b){
        if(b&1)
            ret=(ret*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ret;
}
int finda(int x){
    if(fa[x]==x)
        return x;
    int pa=finda(fa[x]);
    numa[pa]+=numa[x];
    numa[x]=0;
    fa[x]=pa;
    return pa;
}
int findb(int x){
    if(fb[x]==x)
        return x;
    int pa=findb(fb[x]);
    numb[pa]+=numb[x];
    numb[x]=0;
    fb[x]=pa;
    return pa;
}
int main(){
    int n,m,cas=1;
    while(~scanf("%d %d",&n,&m)){
        memset(suma,0,sizeof(suma));
        memset(sumb,0,sizeof(sumb));
        for(int i=0;i<n;++i){
            scanf("%d",&a[i]);
            fa[i]=i;
            numa[i]=1;
        }
        for(int i=0;i<m;++i){
            scanf("%d",&b[i]);
            fb[i]=i;
            numb[i]=1;
        }
        for(int i=0;i<n;++i)
            fa[i]=finda(a[i]);
        for(int i=0;i<m;++i)
            fb[i]=findb(b[i]);
        for(int i=0;i<n;++i)
            finda(i);
        for(int i=0;i<m;++i)
            findb(i);
        for(int i=0;i<n;++i)
            if(finda(i)==i)
                suma[numa[i]]++;
        for(int i=0;i<m;++i)
            if(findb(i)==i)
                sumb[numb[i]]++;
        LL ans=1;
        for(int i=1;i<=n;++i){
            if(suma[i]!=0){
                LL dif=0;
                for(int j=1;j<=sqrt(i*1.0);++j){
                    if(i%j==0){
                        int tmp=i/j;
                        if(sumb[j])
                            dif=(dif+j*sumb[j])%mod;
                        if(sumb[tmp] && tmp!=j)
                            dif=(dif+tmp*sumb[tmp])%mod;
                    }
                }
                dif=quick_pow(dif,suma[i]);
                ans=(ans*dif)%mod;
            }
        }
        printf("Case #%d: %lld\n",cas++,ans);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章