HKE和他的小朋友(矩乘快速冪)


題面:

題目背景:

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熱身賽


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM