Infoplane in Tina Town
Tina Town 的鎮中有一塊表面平整光滑的大石頭, 當人面向它走進時, 石頭的表面就會亮起, 顯示出它的用途. 這塊石頭是前人留下來的東西,是Tina Town算力和控制系統的核心, 同時它也可以顯示Tina Town的大小事和行人關注的內容,也可以作爲公共的計算機使用 ,極大地方便了人們的生活(特別是上街忘帶終端的人們啦).
Tina和Town在這塊大石頭上玩一個遊戲. 石頭上首先顯示出了1到n排列的n個數,Town則隨機交換了一些數, 然後Town通過宏將這個交換的過程記錄下來了. Town 問 Tina: 你知道執行幾次這個宏, 這些數會恢復最早的排列嘛? Tina是一個蠢蠢的女孩子, 當然不知道啦, 於是她想向你請教呢.
由於答案可能很大,你只需要輸出答案對於3∗230+1=3221225473(一個素數)的模就行了.
第一行一個數T,表示數據組數.T≤5 接下來一組數據兩行,第一行一個整數n,表示排列的長度.n≤3×106 第二行一個n個數表示一個排列A1...An,保證數字兩兩不同且對於所有的Ai都有1≤Ai≤n.
對於每組數據輸出一行一個數ans,表示答案.
2 3 1 3 2 6 2 3 4 5 6 1
2 6
數據規模較大建議使用讀入優化.
分析:首先,這道題讀題怎麼都不理解題意,看了幾遍管理員的解釋總算明白了,ORZ。
題意:給一個數組,經過數字循環後,變成原來1~n的遞增序列。數字循環只能是每一次把區間內的第一個數字移到最後(可能也可以把最後的數字移到第一個),幾個區間是同時移動的。
第一個數據 1 3 2 -> 1 2 3 長度爲2 第二個數據 2 3 4 5 6 1 -> 3 4 5 6 1 2 -> 4 5 6 1 2 3 ->
5 6 1 2 3 4 -> 6 1 2 3 4 5 -> 1 2 3 4 5 6 長度爲6
另外數據2
1 4 5 3的過程是 2 1 4 5 3-> 1 2 5 3 4-> 2 1 3 4 5 -> 1 2 4 5 3 -> 2 1 5 3 4-> 1 2 3 4 5 長度爲6
官方題解:
給出一個置換,求它的循環長度。數據範圍 3×106。
其實沒有什麼好說的,就分解成循環求長度的最小公倍數就好了。對於這個模數要用unsigned int存,這個最小公倍數的求法不能用歐幾里得,直接每次分解質因數,用線性篩預處理一下就好了。
時間複雜度:O(n).分析
對於第一部分尋找循環每個點都恰好遍歷一次,O(n);
第二部分分解質因數,時間複雜度爲
∑i=1kf(∣pk∣). f=O(logn),∑i=1k∣pk∣=n.
顯然有f(∣pk∣)≤pk,所以這部分時間複雜度O(n).
第三部分快速冪加乘法同樣分析.
CODE:(這是我直接求最小公倍數,始終沒有發現BUG,但一直WA,ORZ)
#include <iostream>
#include <cstdio>
#include <string.h>
#define ll long long
//#define mod 3221225473
using namespace std;
const ll mod=3221225473;
const int maxn=3000005;
ll n,a[maxn],num[maxn];
bool vis[maxn];
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
int main()
{
//模下gcd是不正確的
int t; scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
int cnt=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){
if(vis[i]) continue;
ll k=i,sum=0;
while(!vis[k]){
vis[k]=1;
k=a[k];
sum++;
}
num[cnt++]=sum;
}
ll gcc=num[0];
for(int i=1;i<cnt;i++){
gcc=gcd(gcc,num[i]);
}
ll ans=num[0]/gcc;
for(int i=1;i<cnt;i++){
ans=(ans*num[i])%mod;
}
if(cnt==1) printf("%lld\n",num[0]);
else printf("%lld\n",ans);
}
return 0;
}
CODE:
#include <iostream>
#include <cstdio>
#include <string.h>
#define ll long long
//#define mod 3221225473
using namespace std;
const ll mod=3221225473;
const int maxn=3000005;
ll n,a[maxn],num[maxn];
bool vis[maxn];
ll qpow_mod(ll x,ll y){
ll ret=1;
while(y){
if(y&1)
ret=(ret*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ret;
}
int main()
{
int t; scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
int cnt=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){
if(vis[i]) continue;
ll k=i,sum=0;
while(!vis[k]){
vis[k]=1;
k=a[k];
sum++;
}
for(int j=2;j<=sum;j++){
int tmp=0;
while(sum%j==0){
tmp++;
sum/=j;
}
if(tmp>num[j])
num[j]=tmp;
}
}
ll ans=1;
for(int i=2;i<=n;i++){
if(num[i])
ans=(ans*qpow_mod(i,num[i]))%mod;
}
printf("%lld\n",ans);
}
return 0;
}
CODE:(很納悶,用了輸入外掛,反而超時了)
#include <iostream>
#include <cstdio>
#include <string.h>
#define ll long long
//#define mod 3221225473
using namespace std;
const ll mod=3221225473;
const int maxn=3000005;
ll n,a[maxn],num[maxn];
bool vis[maxn];
ll qpow_mod(ll x,ll y){
ll ret=1;
while(y){
if(y&1)
ret=(ret*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ret;
}
inline int read(){
char ch;
int num=0;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9')
num=num*10+(ch-'0'),ch=getchar();
return num;
}
int main()
{
int t; scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
int cnt=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
if(vis[i]) continue;
ll k=i,sum=0;
while(!vis[k]){
vis[k]=1;
k=a[k];
sum++;
}
for(int j=2;j<=sum;j++){
int tmp=0;
while(sum%j==0){
tmp++;
sum/=j;
}
if(tmp>num[j])
num[j]=tmp;
}
}
ll ans=1;
for(int i=2;i<=n;i++){
if(num[i])
ans=(ans*qpow_mod(i,num[i]))%mod;
}
printf("%lld\n",ans);
}
return 0;
}