Infoplane in Tina Town
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5392
解题思路:
官方题解:
给出一个置换,求它的循环长度。数据范围 3×106。
其实没有什么好说的,就分解成循环求长度的最小公倍数就好了。对于这个模数要用unsigned int存,这个最小公倍数的求法不能用欧几里得,直接每次分解质因数,用线性筛预处理一下就好了。
时间复杂度:O(n).分析
对于第一部分寻找循环每个点都恰好遍历一次,O(n);
第二部分分解质因数,时间复杂度为
∑i=1kf(∣pk∣). f=O(logn),∑i=1k∣pk∣=n.
显然有f(∣pk∣)≤pk,所以这部分时间复杂度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;
}