實驗室外的攻防戰 UOJ#180 [樹狀陣列]
題目
時針指向午夜十二點,約定的日子——2月28日終於到來了。隨著一聲槍響,伏特跳蚤國王率領著他的跳蚤大軍們包圍了 \(picks\) 博士所在的實驗室。
當然,\(picks\) 博士不會坐以待斃,他早就率領著他的猴子們在實驗室外修築了許多的堅固防禦工事。
經過跳蚤偵察兵的勘察,跳蚤國王發現 \(picks\) 博士的防禦工事有著 \(n\) 處薄弱點,於是他把他的跳蚤大軍分成了 \(n\) 支小隊,並打算讓它們分別進攻每一個薄弱點。但是因為戰場混亂,這 \(n\) 支小隊的位置被打亂了,重新整隊之後,跳蚤國王發現第 \(i\) 個位置的小隊編號為 \(A_i\)(顯然 \(A\) 是一個排列)。
經過計算,跳蚤國王發現,讓第 \(i\) 個位置的小隊編號為 \(B_i\) 時,他的軍隊可以發揮出最大的戰鬥力(保證 \(B\) 也是一個排列)。
跳蚤國王可以發出指令來改變小隊們的排列順序,每一次,他都會報出一個整數 \(i\ (1\leqslant i< n)\) 。如果排在第 \(i\) 個位置的小隊編號大於第 \(i+1\) 個位置的小隊,那麼這兩支小隊會交換順序,否則這一個命令將會被忽略。
現在跳蚤國王希望他的軍隊能夠發揮出最強大的戰鬥力,於是他想要知道是否存在一種指令序列,使得小隊們可以按照排列 \(B\) 的方式排列。
但是因為小隊數目實在是太多,跳蚤國王一時間也沒有看出答案。於是他派跳蚤綁架來了你——這附近最著名的民間科學家來幫他計算這個問題的答案。
輸入格式
輸入資料第一行包含一個正整數 \(n\) 。
接下來兩行每行 \(n\) 個正整數,分別描述排列 \(A\) 和排列 \(B\) 。
輸出格式
對於每組資料,如果存在這樣的指令序列,輸出YES
,否則輸出NO
(引號不輸出,請注意大小寫)。
樣例
樣例輸入1
3
2 3 1
2 1 3
樣例輸出1
YES
explanation
只要報出 \(2\) ,也就是交換第 \(2\) 個位置和第 \(3\) 個位置的小隊即可。
樣例輸入2
3
2 1 3
3 1 2
樣例輸出2
NO
explanation
注意只有相鄰的滿足前一個數大於後一個數的情況下才可以交換。
樣例輸入3
5
4 1 2 5 3
1 2 4 3 5
樣例輸出3
YES
explanation
步驟如下(每次交換的兩個數加粗表示):
4 1 2 5 3
1 4 2 5 3
1 2 4 5 3
1 2 4 3 5
樣例輸入4
5
1 5 3 4 2
1 2 4 3 5
樣例輸出4
NO
樣例輸入5
8
8 2 7 4 5 3 6 1
2 8 5 7 4 3 6 1
樣例輸出5
NO
限制與約定
子任務 | 分值 | 限制與約定 |
---|---|---|
1 | 24 | \(n\leqslant 8\) |
2 | 32 | \(n\leqslant 1000\) |
3 | 44 | \(n\leqslant 100000\) |
對於所有資料,\(1\leqslant n\leqslant 100000\) ,保證輸入的 \(A\) 和 \(B\) 均為一個排列。
時間限制:\(1\ s\)
空間限制:\(256MB\)
分析
又是一個偏序的題,給出一個 \(A\) 序列,讓你判斷能否通過符合要求的轉換,使其轉化成 \(B\) 序列。
我們可以得到這樣一個結論:設有兩個值 \(i\),\(j\) 滿足 \(i<j\) ,如果 \(posa_i < posa_j\) 並且 \(posb_i > posb_j\),那麼這種情況一定是不成立的。證明就是要想從左移到右邊,必須滿足 \(i>j\) 但是這裡 \(i<j\) ,所以一定是不成立的。那麼我們就可以用樹狀陣列來維護一下。
我們記錄下來每個值的位置,並且在修改的時候以 \(A\) 序列中的位置作為下標, \(B\) 序列中的位置作為值,維護一個最大值。這樣我們在查詢的時候列舉數值,每一次查詢 \(A\) 序列中值的位置,查詢到的結果就是 \(i<j\) 且 \(posa_i < posa_j\) 的最大的 \(posb_j\) 這樣我們只需要比較一下這個值和當前列舉到的 \(i\) 值的 \(posb_i\) ,就能得到答案。
程式碼
#include<cstdio>
//以下好多行都是卡常
const int L=1<<20;
char buffer[L],*S,*T;
#define lowbit(x) (x & -x)
#define getchar() (S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T)?EOF:*S++)
#define inline __inline__ __attribute__((__always_inline__))
#define max(a,b) (a>b?a:b)
#define re register
//從這裡開始正經
const int maxn = 1e5+10;
int n;
int t[maxn];
int posa[maxn],posb[maxn];
inline int read(){
re int s = 0,f = 1;
re char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-')f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
s = (s<<3) + (s<<1) + ch - '0';
ch = getchar();
}
return s * f;
}
inline void modify(re int x,re int val){//樹狀陣列維護最大值
while(x <= n){
t[x] = max(t[x],val);
x += lowbit(x);
}
}
inline int query(re int x){//查詢
re int ans = 0;
while(x){
ans = max(ans,t[x]);
x -= lowbit(x);
}
return ans;
}
int main(){
n = read();
for(re int i=1;i<=n;++i){//記錄值在A序列中的位置
posa[read()] = i;
}
for(re int i=1;i<=n;++i){//記錄值在B序列中的位置
posb[read()] = i;
}
for(re int i=1;i<=n;++i){//列舉數值,依次查詢A序列中i的位置
if(query(posa[i]) > posb[i]){//如果有上邊所說的衝突情況直接NO
puts("NO");
return 0;
}
modify(posa[i],posb[i]);
}
puts("YES");//沒有輸出NO就是合法的
return 0;
}