實驗室外的攻防戰 UOJ#180 [樹狀陣列]

Vocanda發表於2020-08-15

實驗室外的攻防戰 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;
}

相關文章