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);
    }
}


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