【HDU 6038】Function(图+组合数学)

6 篇文章 0 订阅

题目链接:hdu6038

题目大意:a数组有n个元素(0 ~ n-1),b数组有m个元素(0 ~ m-1),定义一个函数 f ,使 f(i) = b[f(ai)],求满足条件的 f  函数有多少种

                 例如     3   2
                      a     1   0   2                f (0) = b [f(1)]                        所以可能的结果有 4   (2*2) 种,
                      b     0   1                     f (1) = b [f(0)]                        ① f (0) = f (1) = 0   或   f (0) = f (1) = 1

                                                         f (2) = b [f(2)]                        ② f (2) = 0    或    f (2) = 1 


思路:a 和 b 中的每个元素都属于一个环,可以发现只有当 b 里环数为 a 里环数 的因数时,可以将 b 环中的值填入 a 环里,对于 b 环,每个环的元素个数就是这个环填入一个a环时的情况数(举例可知),例如:a 形成的环(假设均为m个): 2 环, 3 环,4 环;  b 形成的环(假设均为k个): 1 环,2 环, 3 环, 4 环。则将b环中的数填入a环中:

                                      2 环(a)里包含: 1、2环 (b) ,        每个环有    1*k1 + 2*k2  种情况, 则 m 个环共有    (1*k1 + 2*k2 )^ m1  种情况

                                      3 环包含 1、3环;    4 环包含1、2、4环

CODE:

#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
#define maxn 100005
#define mod (1000000000+7)
typedef long long ll;

using namespace std;

ll mp[2][maxn];
ll vis[maxn], f[maxn];

ll fun(ll x, ll n)
{
	ll ans = 1;
	while (n) {
		if (n & 1) ans = (ans*x) % mod;
		x = x*x%mod;
		n >>= 1;
	}
	return ans;
}
void Ring(int n, int u)
{
	memset(vis, 0, sizeof(vis));
	for (int i = 0; i < n; i++)
		scanf("%lld", &f[i]);
	for (int i = 0; i < n; i++) {
		int k = i, cot = 0;
		if (k == f[k] && !vis[k]) {
			mp[u][1]++;
			continue;
		}
		if (!vis[k]) {
			while (k != f[k] && !vis[k]) {
				vis[k] = 1;
				k = f[k];
				cot++;
			}
			if (k == f[k] && !vis[k])
				continue;
			mp[u][cot]++;
		}
	}
}

ll k[maxn],a[maxn],numa[maxn],b[maxn],numb[maxn];
int main()
{
	int n, m, cas = 1;

	while (~scanf("%d%d", &n, &m)) {
		memset(mp, 0, sizeof(mp));
		memset(k, 0, sizeof(k));
		Ring(n, 0);   //得到环,mp[0][k]表示a中k环的个数
		Ring(m, 1);   //mp[1][k]表示b中k环的个数
		ll ans = 1,cota = 1,cotb = 1;
		for (int i = 1; i <= n || i <= m; i++) {   //o(n)将所需的数列出,降低循环次数
			if (mp[0][i]) {
				a[cota] = mp[0][i];
				numa[cota++] = i;
			}
			if (mp[1][i]) {
				b[cotb] = mp[1][i];
				numb[cotb++] = i;
			}
		}
		for (int i = 1; i < cota; i++) {
			for (int j = 1; j < cotb; j++) {
				if (numa[i] % numb[j] == 0) {
					k[i] = (k[i]%mod + numb[j]*b[j] % mod) % mod;     //1个a环的所有情况加起来
				}
			}
		}
		for (int i = 1; i < cota; i++) {
			ans *= fun(k[i], a[i]);
		}
		printf("Case #%d: %lld\n", cas++, ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值