題面:
題目背景:
HKE帶着 $ n $ 個小朋友做游戲
題目描述:
現在有n個座位編號為 $ 1 $ 至 $ n $ ,這些小朋友也編號 $ 1 $ 至 $ n $ 。一開始所有小朋友都坐在相應的座位上。HKE的游戲可用一個n的排列 $ A(A_1,A_2\cdots A_n $ )表示。一輪游戲時,對於所有的 $ 1\leq i\leq n $ ,坐在位置 $ i $ 上的小朋友坐到位置 $ A_i $ 上。
現在游戲進行了 $ k $ 輪,HKE想知道游戲結束后,位置 $ 1,2\cdots n $ 分別坐了幾號小朋友?
輸入格式:
第一行 $ n,k $ 。
第二行 $ A_1,A_2\cdots A_n $
輸出格式:
一行n個數表示 k 輪游戲后坐在位置 $ 1,2……n $ 上的小朋友的編號
輸入樣例#1:
5 5
2 3 1 5 4
輸出樣例#1:
2 3 1 5 4
輸入樣例#2:
5 4
2 3 1 5 4
輸出樣例#2:
3 1 2 4 5
數據范圍:
30%的數據, $ n\leq1000 $ , $ k\leq1000 $
100%的數據, $ n\leq100000 $ , $ k\leq2^{31}-1 $
本題 $ solution $ :
首先請允許我奶一波:本題出的是真的好!
某蒟蒻心路歷程:
小文:這不是矩陣快速冪裸題嗎?!!
題面:矩陣快速冪復雜度 $ n^3 $ OK?
小文:那就降到二維 $ n^2 $ 優化麻!!!
題面:.... $ n\leq100000 $ ,are you sure?
小文:我 &% $ &#%&#% !!!!!!!
於是乎讓我再仔細看看題目吧:
解 1 :跑圖論求環
我們(在腦海里)建一個圖,將第 i 步的結果與第 i+1 步的結果用一條邊連起來,跑一遍你會發現這是一個環(即你不斷轉換下去會回到你的初始狀態。所以你將 k mod 一下環的大小( $ \leq n $ )然后跑一遍圖即可。(稍稍維護一下復雜度)
這對蒟蒻來說太難了,於是就沒有代碼實現了
解 2 :快速冪
這題其實不存在矩陣成分(有啟發效果),重點在與快速冪和你的轉移過程。
原理:
1.轉移的結合律:
下文中但凡以 2 3 1 5 4(一個栗子)為標准轉移,2 3 1 5 4 分別表示 $ A_1 $ $ A_2 $ $ A_3 $ $ .... $ $ A_5 $ :
轉移的實現:
inline void ans(){ //給ans數組轉換
for(rg i=1;i<=n;++i) c[i]=a[i];
for(rg i=1;i<=n;++i) a[b[i]]=c[i];
}
對於一個以 2 3 1 5 4 為標准的轉移,我們若轉移兩次,就相當於進行一次以 3 1 2 4 5 為標准的轉移(不信試試);而我們若轉移 3 次,就相當於先進行一次以 3 1 2 4 5 為標准的轉移在進行一次以 2 3 1 5 4 為標准的轉移。這說明小朋友換位置具有結合律,這是我們快速冪的基礎。
而 3 1 2 4 5 是可以通過 2 3 1 5 4 推出來的(類似矩陣乘法):
$ \begin{vmatrix}2 &3&1&4&5\\2&3&1&4&5\|&|&|&|&|\\3&1&2&4&5\end{vmatrix} $ $
看的出怎么推嗎:先講一下 3 是如何來的:
首先 3 的意義表示 1 號小朋友在轉移兩次后在 3 號位置。所以我們看到 1 號小朋友第一輪轉換時要轉換到 2 號位置,而第二輪轉換時 2 號位置的人要轉換到 3 號位置,所以就相當於一號小朋友在轉移兩次后要在 3 號位置。 1 2 4 5 也是這樣得來的:實現:
inline void base(){ //給bese數組轉換
for(rg i=1;i<=n;++i) c[i]=b[i];
for(rg i=1;i<=n;++i) b[i]=c[c[i]];
}
而此時如果我們需要以2 3 1 4 5為標准轉移 4 次,就可以直接以 3 1 2 4 5為標准轉移兩次即可。同樣我們還可以用 3 1 2 4 5來推出一個序列,以次序列為標准轉移就能直接得到以2 3 1 4 5轉移 4 次的結果。
然后直接快速冪求解即可!
代碼實現:
以某一轉換序列來推出下一個轉換序列,我們用base函數實現。
以某一序列為標准轉移,我們用ans函數實現。(這兩個不一樣!!
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#define rg register int
using namespace std;
int n,k;
int b[100001];// base
int a[100001];// answer
int c[100001];// 借來轉換賦值
inline int qr(){ char ch; // 快讀
while((ch=getchar())<'0'||ch>'9');
int res=ch^48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch^48);
return res;
}
inline void ans(){ //給ans數組轉換
for(rg i=1;i<=n;++i) c[i]=a[i];
for(rg i=1;i<=n;++i) a[b[i]]=c[i];//重點1
}
inline void base(){ //給bese數組轉換
for(rg i=1;i<=n;++i) c[i]=b[i];
for(rg i=1;i<=n;++i) b[i]=c[c[i]];//重點2
}
int main(){
n=qr();k=qr();
for(rg i=1;i<=n;++i)
b[i]=qr(),a[i]=i;//賦初值
while(k){
if(k&1)ans();
base();k>>=1;
}// 快速冪
for(rg i=1;i<=n;++i)
printf("%d ",a[i]);//輸出ans
return 0;
}
本題重在理解,碼量其實不高(除去快讀等....
$ O_{(n\log{n})} $ 的復雜度加上碼量還是很優秀的。
注:題目來源:華南師范大學附屬中學,洛谷Noip熱身賽