题意:给出一个0..n-1的全排列a,给出一个0..m-1的全排列b,现在要求计算函数F(定义域0..n-1 值域0..m-1)的个数,使得对所有的0..n-1的 i 都有 F(i)=b(F(ai))
题解:现在已知置换a和b,我们考虑一下这个长相奇特的限制条件:b是一个已知的函数,这个式子的意思是F(ai)映射到F(i)就像b那样子,用更离散数学的说法讲就是:F和b有点同构的意思(同构还需要把f和b互换的另外那个条件才行)。
我们从F的角度出发:每个F(ai)被映射到了F(i),而a是一个置换,因此这个映射关系是一个图,图上只有若干裸环(每个点的入度=出度=1).而这个关系必须在b下成立。那么再来考虑b:b也是一个置换,他也是若干裸环。
现在题目变成了:置换a的裸环要在置换b下成立。我们考虑a中的一个环,假设他的长度是LenA,他在b下成立,也就是它满足于b中的一个环,假设长度是LenB。而满足的意思就是:不妨让a和b中某两个点等价,整个换推一遍,不出现冲突就可以了。那么很显然,LenA%LenB==0才能满足这个条件。
当LenA环和LenB环匹配成功,他们贡献了多少方案数呢?不妨取LenA的一个点做赋值起点,他可以选择和LenB中任意一个点等价,那么贡献的方案数就是LenB,而假设b中LenB的环有NumB个,于是单独一个a中的LenA环的所有贡献就是∑LenB*NumB(LenA%LenB=0)
所以最后的算法流程就是:分别求出A和B的所有循环节长度。其中B的要计数,计数完了变成LenB*NumB。然后对于每个LenB,找到他的所有银子Lb,把答案累加到LenB上去,然后对于A的LenA,累加答案就妥了。
Code:
#include<bits/stdc++.h>
using namespace std;
#define MAX 100050
#define MOD 1000000007
int Ea[MAX],Eb[MAX];
long long R[MAX];
int m,n;
bool visa[MAX],visb[MAX];
vector<int> Ra;
void dfsbb(int nod,int num,int root){
if (nod==root&&num!=0){
R[num]++;
return;
}else{
visb[nod]=true;
dfsbb(Eb[nod],num+1,root);
}
}
void dfsb(){
for (int i=0;i<m;i++){
if (!visb[i]){
dfsbb(i,0,i);
}
}
}
void dfsaa(int nod,int num,int root){
if (nod==root&&num!=0){
Ra.push_back(num);
return ;
}else{
visa[nod]=true;
dfsaa(Ea[nod],num+1,root);
}
}
void dfsa(){
for (int i=0;i<n;i++){
if (!visa[i]){
dfsaa(i,0,i);
}
}
}
int main(){
int num =1;
// freopen("1006.in","r",stdin);
// freopen("1006.out","w",stdout);
while (scanf("%d%d",&n,&m)!=EOF){
memset(R,0,sizeof(R));
memset(visa,false,sizeof(visa));
memset(visb,false,sizeof(visb));
Ra.clear();
for (int i=0;i<n;i++){
int temp;
scanf("%d",&temp);
Ea[temp] = i;
}
for (int i=0;i<m;i++){
int temp;
scanf("%d",&temp);
Eb[i]=temp;
}
dfsb();
dfsa();
for (int i=n;i>=1;i--){
R[i] = i*R[i]%MOD;
for (int j = i+i;j<=n;j+=i){
R[j]+=R[i];
R[j]%=MOD;
}
}
long long ans =1;
for (vector<int>::iterator it = Ra.begin();it!=Ra.end();it++){
ans*=R[*it];
ans%=MOD;
}
cout<<"Case #"<<num++<<": "<<ans<<endl;
}
return 0;
}