hdu 5392 Infoplane in Tina Town

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5392

解题思路:

官方题解:

给出一个置换,求它的循环长度。数据范围 3\times 10^63×106

其实没有什么好说的,就分解成循环求长度的最小公倍数就好了。对于这个模数要用unsigned int存,这个最小公倍数的求法不能用欧几里得,直接每次分解质因数,用线性筛预处理一下就好了。

时间复杂度:O(n)O(n).分析

对于第一部分寻找循环每个点都恰好遍历一次,O(n)O(n);

第二部分分解质因数,时间复杂度为

\sum_{i=1}^kf(|p_k|)i=1kf(pk)f=O(\log{n})f=O(logn),\sum_{i=1}^k|p_k|=ni=1kpk=n.

显然有f(|p_k|)\le p_kf(pk)pk,所以这部分时间复杂度O(n)O(n).

第三部分快速幂加乘法同样分析

郁闷呀,今天试了一下这个函数

inline void read(int &x) {
    char c;
    x = 0;
    while((c=getchar()) < '0' || c > '9');
    while(c>='0'&&c<='9')
        x = x*10+(c-'0'),c = getchar();
}
结果一直超时,改成scanf反而不超时了。。。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define MOD 3221225473
using namespace std;

typedef long long ll;
const int maxn = 3000005;
int a[maxn],vis[maxn],num[maxn];

ll mod_pow(ll x,int n,ll mod){
    ll res = 1;
    while(n){
        if(n&1)
            res = (res*x)%mod;
        x = (x*x)%mod;
        n >>= 1;
    }
    return res;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i = 1; i <= n; i++)
            scanf("%d",&a[i]);
        memset(vis,0,sizeof(vis));
        memset(num,0,sizeof(num));
        ll ans = 1;
        for(int i = 1; i <= n; i++){
            if(vis[i])
                continue;
            int k = i,sum = 0;
            while(!vis[k]){
                vis[k] = 1;
                k = a[k];
                sum++;
            }
            for(int i = 2; i <= sum; i++){
                int tt = 0;
                while(sum % i == 0){
                    sum /= i;
                    tt++;
                }
                if(tt > num[i])
                    num[i] = tt;
            }
        }
        for(int i = 2; i <= n; i++){
            if(num[i])
                ans = ans*mod_pow((ll)i,num[i],(ll)MOD)%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


你可能感兴趣的:(快速幂,分解质因数)