題解 NowCoder7225A 排列

2022-05-23 11:12:10 字數 1411 閱讀 8059

牛牛拿到了乙個長度為n的排列和m個區間,一開始排列是1、2、3......n。

然後他將這些區間在按順序在排列上翻轉,全部翻轉一遍稱一次操作。

現在他要去搞文化了...所以拜託你告訴他經過k次操作後的排列長什麼樣子。

第一行三個整數:n,m,k。

接下來m行,每行兩個整數l和r描述乙個區間。

輸出一行n個數,表示經過k次操作後的排列。

一開始,我認為對於任何的操作,只要操作兩遍就等於沒有操作,也就是說所有偶數次的操作都可以被忽略,然而……

事實證明,我這個方法是不對的,這題也不可能這麼簡單(實際上我到現在沒有找到反例)

那麼換種思路

一開始我想到的是,可以先翻轉一次,記錄這個陣列的變化,也就是哪些位置發生了交換。

之後,就可以直接使用這個變化量來迴圈操作k次了,時間複雜度為o(nk)

但是按照這個資料規模,這個時間複雜度一定沒有辦法ac,怎麼辦呢?

眾所周知,倍增是乙個特別快的演算法,時間複雜度可以降低到對數級,著名的快速冪演算法和st表使用的就是倍增思想。

稍加思考,很容易發現,求出2此操作後的變化量,可以把操作一次之後的結果在按照這個變化量變化,這樣就可以得到操作2次的變化量。

然後,我們就可以利用操作2次之後的結果 來得出操作兩次後 陣列的變化量,進而得出操作4次之後的結果。

如此往復,可以通過遞推的方式得到2^n次操作之後的變化量。

又因為任何乙個數都可以拆分成2^a[i]的和(其中a[i]為自然數)。

比如5次操作可以通過先進行1此操作,再進行4次操作實現,這和快速冪演算法是非常接近的。

於是,我們可以使用記錄變化量和倍增的方式達到o(n log k)的時間複雜度。

#include #include #include using namespace std;

const int max=100005;

int main()*/

for(int i=1; i<=n; i++) pre[i]=i;

for(int i=0; i>1); j++)

} int now[max], ans[max], ansbak[max];

for(int i=1; i<=n; i++) now[i]=pre[i];

for(int i=1; i<=n; i++) ans[i]=i;

while(k)

for(int i=1; i<=n; i++)

} for(int i=1; i<=n; i++)

for(int i=1; i<=n; i++) pre[i]=now[i];

k>>=1;

} for(int i=1; i<=n; i++) printf("%d ", ans[i]);

return 0;

}

nowcoder提高組2題解

化一下試子就ok include includeinline long long read while c 9 c 0 x x 10 c 0 c getchar return x f long long n long long a 100007 long long sum 100007 long l...

錯排問題解法

背景 同室四人各寫一張賀卡,先集中起來,然後每人從中拿一張別人送出的賀卡,四張賀卡的不同分配方式有多少種?問題 錯排問題 有n個正整數1,2,3,n,將這n個正整數重新排列,使其中的每乙個數都不在原來的位置上,這種排列稱為正整數1,2,3,n的錯排,問這n個正整數的排列方法有多少種?遞推公式f n ...

錯排問題解法

背景 同室四人各寫一張賀卡,先集中起來,然後每人從中拿一張別人送出的賀卡,四張賀卡的不同分配方式有多少種?問題 錯排問題 有n個正整數1,2,3,n,將這n個正整數重新排列,使其中的每乙個數都不在原來的位置上,這種排列稱為正整數1,2,3,n的錯排,問這n個正整數的排列方法有多少種?遞推公式f n ...