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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值