版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/84791456
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6038
题意:
有点复杂,耐心观看。给你一个长度为n下标从0~n-1的a数组,和一个长度为m下标从0~m-1的b数组,两个数组均为一组排序(即0~n-1种每一个数都只出现一次)。要你找出有多少种函数f的映射,使得对,都有成立。
做法:
重点就在这两个数组都是排序!所以对于a和b数组来说,里面的数字可以组成至少一个环,什么意思呢。假设我们在a数组中有a[1]=3,a[3]=6,a[6]=1.那么这就有1->3,3->6,6->1的一个环,环的大小(即环内的节点数)就是3。由于说明是排序,所以保证两数组中均有至少一个两两不相交的环,我们将这些环的大小和记录下来后,会发现,在我们对0~n-1进行处理时,如果a中有一个环的大小为x,b中有一个环的大小为y并且有x%y=0使,那么y中的数字可以成为x映射的对象。假设有a中有1->2,2->3,3->4,4->5,5->6,6->1,b中有 0->5,5->0时,令f(1)=0,自然就有f(2)=5,f(3)=0...,这便是一组映射,当f(1)=5时,能得到不同的映射。那么答案自然就出来了。
我们枚举a里的每一个环,去找是否存在b里有环数等于它的因子x,如果有,那么这个环的方案要加上这个因子x*x出现的次数,最后a里每一个环的数量相乘即可。有个小细节,如果有一个环它没有可行,那么最终为0
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=100005;
vector<ll> yinzi[maxn];
ll a[maxn],b[maxn],n,m;
map<ll,ll> numb,numa;
map<ll,ll>::iterator it;
bool visa[maxn],visb[maxn];
void init(){
yinzi[1].pb(1);
for(int i=2;i<maxn-2;i++){
yinzi[i].pb(1ll);
for(int j=i;j<maxn;j+=i){
yinzi[j].pb((ll)i);
}
}
}
ll quick(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int main(){
init();
int cas=0;
while(~scanf("%lld%lld",&n,&m)){
memset(visa,0,sizeof(visa));
memset(visb,0,sizeof(visb));
numb.clear();numa.clear();
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<m;i++)
scanf("%lld",&b[i]);
for(int i=0;i<n;i++){
ll innum=0;
if(!visa[i]){
int now=i;
while(!visa[now]){
innum++;
visa[now]=1;
now=a[now];
}
numa[innum]++;
}
}
for(int i=0;i<m;i++){
int innum=0;
if(!visb[i]){
int now=i;
while(!visb[now]){
innum++;
visb[now]=1;
now=b[now];
}
numb[innum]++;
}
}
ll ans=1;
for(it=numa.begin();it!=numa.end();it++){
ll aim=it->first,num=it->second;
//cout<<aim<<" "<<num<<endl;
ll tmpans=0;
for(int i=0;i<yinzi[aim].size();i++){
ll yi=yinzi[aim][i];
tmpans+=(ll)numb[yi]*yi;
}
ans=(ans*quick(tmpans,num))%mod;
}
printf("Case #%d: %lld\n",++cas,ans);
}
return 0;
}