【hdu 6038】Function

Link:http://codeforces.com/contest/834/problem/C

Description

给你两个排列a和b;
a排列的长度为n,b排列的长度为m;
a∈[0..n-1],b∈[0..m-1];
然后让你求一个函数f[i];
f[i]的定义域为0..n-1,值域为0..m-1
同时使得对于任意f[i],i∈[0..n-1];
f(i)=bf(a[i])成立;

Solution

原始可以递推一下;
f(i)=bf(ai)=bbf(aai)
则可以一直写下去f[i]=bbbbbf(aaaaa[i]);
注意到a是一个排列;
最后肯定能形成一个环,则aaaaa..a[i]肯定又能变回i

f(i)=bbf(i)��l times b
(这里L是第一次回到i的L);
这里的含义其实就相当于f[i]是一个x
要使得
x=b....bx
而b也是一个排列;
也肯定有循环节;
这里从x开始的b数组的循环节长度一定得是上面的a的循环节的长度L的因子;
不然就不能在L次b之后回到x了;
于是,
a数组里找循环节的长度,在b数组中也找循环节的长度;
看看有多少个长度在a中有,且b数组中,有它的因子长度的循环节;
直接累加因子循环节长度到temp中;
然后累乘所有temp即可;
根据上面的形式,每个a循环节中的某一个位置,f只要确定了,其他该循环节中的f值也就确定了,然后那个位置有temp种选择;就是因子循环节中任意一个b[i]都可以;
找因子的时候,需要做些优化;
不然可能退成O(n2)的复杂度;
先枚举a数组有哪些循环节,长度为i;
然后用O(i12)复杂度枚举它可能的因子,(j是则n/j也是)
看看在b中有没有这样长度的;

NumberOf WA

0

Reviw

求因子的思想很好.

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 1e5;
const int MOD = 1e9+7;

int n,m;
int a[N+10],b[N+10],cnta[N+10],cntb[N+10];
bool flag[N+10];

main(){
    int kk = 0;
    while (~scanf("%lld%lld",&n,&m)){
        for (int i = 1;i <= n;i++){
            scanf("%lld",&a[i]);
            a[i]++;
        }
        for (int i = 1;i <= m;i++){
            scanf("%lld",&b[i]);
            b[i]++;
        }

        memset(cnta,0,sizeof cnta);
        memset(cntb,0,sizeof cntb);

        memset(flag,0,sizeof flag);
        for (int i = 1;i <= m;i++)
            if (!flag[i]){
                int x = i,num = 0;
                while (!flag[x]){
                    flag[x] = 1;
                    num++;
                    x = b[x];
                }
                cntb[num]++;
            }
        memset(flag,0,sizeof flag);
        for (int i = 1;i <= n;i++)
            if (!flag[i]){
                int x = i,num = 0;
                while (!flag[x]){
                    flag[x] = 1;
                    num++;
                    x = a[x];
                }
                cnta[num]++;
            }

        int ans = 1;
        for (int i = 1;i <= n;i++)
        if (cnta[i]>0){
            int temp = 0;
            for (int j = 1;j*j <= i;j++)
                if (i%j==0){
                    temp = (temp + j*cntb[j])%MOD;
                    if (j != i/j)
                        temp = (temp + (i/j)*cntb[i/j])%MOD;
                }
            while (cnta[i]--){
                ans = (ans*temp)%MOD;
            }
        }
        printf("Case #%lld: %lld
",++kk,ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AWCXV/p/7626148.html