2017年多校赛第一场 1006 Function(枚举)

这道题所有情况满足的情况下会构成一个环,我们很容易想到枚举环中的一个点,环中的其他点就都可以得出了。

这道题是问一共有多少种所有情况都满足的情况。所以我们先预处理a里面的循环节,统计一下长度为i的循环节有几个。然后预处理b里面的循环节,同样统计长度为i的循环节有几个。

最后枚举一下,假设a数组中有循环节长度为i,那么在b中枚举循环节为i的因子的。比如j为i的因子,那么此时就会多出j * num_b[j]种情况。枚举完之后,得到a数组中循环节为i时有tmp种情况。那么再看a中有多少个循环节长度为i。假设有两个,也就是a中有两个环同时都有tmp种情况,那么总的情况数就是tmp^2种,答案ans * tmp^2即可。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)
#define eps 1e-9

typedef long long ll;
typedef pair<int, int> pii;

const int INF = 0x7fffffff;
const ll mod = 1e9 + 7;
const int maxn = 1e5 + 5;
bool vst[maxn];
int n, m;
int a[maxn], b[maxn];
int nx[maxn];
ll num_a[maxn], num_b[maxn];

int main() {
    int Case = 1;
    while(cin >> n >> m) {
        
        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 < n; i++) {
            nx[a[i]] = i;
        }

		memset(vst, 0, sizeof(bool) * maxn);
		memset(num_a, 0, sizeof(num_a));
		memset(num_b, 0, sizeof(num_b));
		
        for(int i = 0; i < n; i++) {
        	if(!vst[i]) {
        		int now = nx[i];
        		int cnt = 1;
        		vst[now] = 1;
        		while(now != i){
        			now = nx[now];
        			vst[now] = 1;
        			cnt++;
        		}
        		num_a[cnt]++;
			}
		}
		
		memset(vst, 0, sizeof(vst));
		
		for(int i = 0; i < m; i++) {
        	if(!vst[i]) {
        		int now = b[i];
        		int cnt = 1;
        		vst[now] = 1;
        		while(now != i){
        			now = b[now];
        			cnt++;
        			vst[now] = 1;
        		}
        		num_b[cnt]++;
			}
		}
		
		ll ans = 1;
        for(int i = 1; i <= n; i++) {
        	if(num_a[i]) {
        		int Max = sqrt(i + 0.5);
        		ll tmp = 0;
        		for(int j = 1; j <= Max; j++) {
	            	if(i % j == 0) {
	            		tmp += (j * num_b[j]) % mod;
	            		if(j * j != i)
	            			tmp += ((i / j) * num_b[i / j]) % mod;
	            	}
				}
				for(int j = 0; j < num_a[i]; j++)
					ans = ans * tmp % mod; 
			}
        }
        printf("Case #%d: %I64d\n", Case++, (ans % mod));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值