【HDU6038】Function-思维+组合数学

测试地址:Function
题目大意: 给出一个关于 0 0 0 n − 1 n-1 n1的置换 a a a,一个关于 0 0 0 m − 1 m-1 m1的置换 b b b,求有多少从 0 0 0 n − 1 n-1 n1映射到 0 0 0 m − 1 m-1 m1的映射 f f f,满足 f ( i ) = b f ( a i ) f(i)=b_{f(a_i)} f(i)=bf(ai)
做法: 本题需要用到思维+组合数学。
根据题目的要求, f ( a i ) f(a_i) f(ai)可以通过置换 b b b成为 f ( i ) f(i) f(i),所以我们对于置换 a a a画一个反向循环图(即,从点 i i i可以走到点 a i a_i ai,反向就是从点 a i a_i ai走到点 i i i),再对置换 b b b画一个循环图,在两张图中分别选出两个点 x , y x,y x,y,代表 f ( x ) = y f(x)=y f(x)=y,那么可以发现,当 x x x走一步, y y y也走一步, f ( x ) = y f(x)=y f(x)=y应该还是成立的。这也就说明, y y y b b b中的循环长度,应该是 x x x a a a中循环长度的因数,这样才能保证 f ( x ) = y f(x)=y f(x)=y在走的过程中总是成立。而因为初始的 y y y又有 l e n ( y ) len(y) len(y)种选择( l e n ( y ) len(y) len(y) y y y所在循环的长度),因此我们找到了一种统计答案的方法:对一个 a a a中的环,在 b b b中找到所有环长为这个环长的因数的环,累加 l e n ( y ) len(y) len(y)。直接统计因数的贡献比较麻烦,我们反过来考虑每个 b b b中的环,它对环长为 l e n ( y ) len(y) len(y)的倍数的环有 l e n ( y ) len(y) len(y)贡献,我们只需要一开始算出 c o u n t i count_i counti,表示环长为 i i i b b b中的环的个数,就可以 O ( m log ⁡ m ) O(m\log m) O(mlogm)计算贡献了。那么对于 a a a中的每个环,可以计算出这个环的方案数,运用乘法原理把每个环的方案数乘起来就行了。
我傻逼的地方:我又分不清楚 n n n m m m了…这错误一次比一次觉得傻逼…
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
int n,m,a[100010],b[100010],tot;
ll cnt[100010],tmp[100010];
bool vis[100010];

void find_loop(int *nxt,int v)
{
	while(!vis[v])
	{
		vis[v]=1;tot++;
		v=nxt[v];
	}
}

int main()
{
	int t=0;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);
		for(int i=0;i<m;i++)
			scanf("%d",&b[i]);
		
		for(int i=0;i<=m;i++)
			tmp[i]=vis[i]=0;
		for(int i=0;i<m;i++)
			if (!vis[i])
			{
				tot=0;
				find_loop(b,i);
				tmp[tot]++;
			}
		for(int i=1;i<=n;i++)
			cnt[i]=0;
		for(int i=1;i<=m;i++)
			for(int j=1;i*j<=n;j++)
				cnt[i*j]=(cnt[i*j]+tmp[i]*(ll)i)%mod;
		
		ll ans=1;
		for(int i=0;i<n;i++)
			vis[i]=0;
		for(int i=0;i<n;i++)
			if (!vis[i])
			{
				tot=0;
				find_loop(a,i);
				ans=ans*cnt[tot]%mod;
			}
		printf("Case #%d: %lld\n",++t,ans);
	}
	
	return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值