2017 Multi-University Training Contest - Team 1 1006 Function(思维 循环节)

题意:给你一个序列a,是0到n-1的一个排列,给你一个序列b,是0到m-1的一个排列,问你有多少种不同的函数关系满足:f[i] = b[f[a[i]]] (0 < i < n).


官方题解:考虑置换 aa 的一个循环节,长度为 ll ,那么有 $f(i) = b_{f(a_i)} = b_{b_{f(a_{a_i})}} = \underbrace{b_{\cdots b_{f(i)}}}_{l\text{ times }b}$ 。

那么 f(i)f(i) 的值在置换 bb 中所在的循环节的长度必须为 ll 的因数。

而如果 f(i)f(i) 的值确定下来了,这个循环节的另外 l - 1l1 个数的函数值也都确定下来了。

答案就是 \sum_{i = 1}^{k} \sum_{j | l_i} {j \cdot cal_j}i=1kjlijcalj 改为 \prod_{i = 1}^{k} \sum_{j | l_i} {j \cdot cal_j}i=1kjlijcalj ,其中 kk 是置换 aa 中循环节的个数, l_ili 表示置换 aa 中第 ii 个循环节的长度, cal_jcalj 表示置换 bb 中长度为 jj 的循环节的个数。

时间复杂度是 O(n + m) 。

官方的题解有些乱码。。。。可以参考帅神的博客:点击打开链接


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 1e6+5;
int a[maxn], b[maxn];
int n, m;
bool visA[maxn], visB[maxn];
int numA[maxn], numB[maxn];

int main(void)
{
    int ca = 1;
    while(cin >> n >> m)
    {
        memset(numA, 0, sizeof(numA));
        memset(numB, 0, sizeof(numB));
        memset(visA, 0, sizeof(visA));
        memset(visB, 0, sizeof(visB));
        for(int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        for(int i = 0; i < m; i++)
            scanf("%d", &b[i]);
        int cntA = 0, cntB = 0;
        for(int i = 0; i < n; i++)
        {
            int len = 0;
            int tmp = i;
            while(!visA[tmp])
            {
                len++;
                visA[tmp] = 1;
                tmp = a[tmp];
            }
            if(len)
                numA[cntA++] = len;
        }
        for(int i = 0; i < m; i++)
        {
            int len = 0;
            int tmp = i;
            while(!visB[tmp])
            {
                len++;
                visB[tmp] = 1;
                tmp = b[tmp];
            }
            if(len) numB[len]++;
        }
        ll ans = 1;
        for(int i = 0; i < cntA; i++)
        {
            ll tmp = 0;
            for(int j = 1; j*j <= numA[i]; j++)
            {
                if(numA[i]%j == 0)
                {
                    if(j*j == numA[i])
                        tmp += numB[j]*j;
                    else
                        tmp += numB[j]*j+numB[numA[i]/j]*numA[i]/j;
                }
            }
            ans = ans*tmp%mod;
        }
        printf("Case #%d: %lld\n", ca++, ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值