HDU 3068 2017 Multi-University Training Contest - Team 1 1006 Fuction: 循环节+最后一步

题意:给出一个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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值