牛牛拿到了乙個長度為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 ...