1. 程式人生 > 實用技巧 >特殊堆疊(線段樹題解)

特殊堆疊(線段樹題解)

堆疊是一種經典的後進先出的線性結構,相關的操作主要有“入棧”(在堆疊頂插入一個元素)和“出棧”(將棧頂元素返回並從堆疊中刪除)。本題要求你實現另一個附加的操作:“取中值”——即返回所有堆疊中元素鍵值的中值。給定 N 個元素,如果 N 是偶數,則中值定義為第 N/2 小元;若是奇數,則為第 (N+1)/2 小元。

輸入格式:

輸入的第一行是正整數 N(105\le 10^5)。隨後 N 行,每行給出一句指令,為以下 3 種之一:

Push key
Pop
PeekMedian

其中 key 是不超過 10510^5 的正整數;Push 表示“入棧”;Pop 表示“出棧”;PeekMedian

表示“取中值”。

輸出格式:

對每個 Push 操作,將 key 插入堆疊,無需輸出;對每個 PopPeekMedian 操作,在一行中輸出相應的返回值。若操作非法,則對應輸出 Invalid

輸入樣例:

17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop

輸出樣例:

Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid
作者陳越單位浙江大學程式碼長度限制16 KB時間限制400 ms記憶體限制64 MB

如題所示:
正解:線段樹or樹狀陣列;
剛開始想這題想不明白該怎麼使用資料結構來解決,然後看了一個大佬樹狀陣列的題解,觀察到本題是一個類似於桶排序的方法來解決,由於線段樹能維護具有可加性的資料,因為此題範圍[1,1e5]那麼建樹就利用的是每個整數值在序列中出現的次數,二分查詢[1,mid]區間出現次數,即可求解;
程式碼如下:


/*************************************************************************
    > File Name: pta.cpp
# Author: Badwoman
# mail: [email protected]
    > Created Time: 2020年11月26日 星期四 15時19分09秒
 ************************************************************************/

#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
using namespace std;
const int N = 1e6+10,inf = 1e5;
int sum[N];
int n,tot,now[N/10];char op[99];
void add(int s,int t,int k,int y,int p){
	int mid = (s+t)>>1;
	if(s==k&&t==k){
		sum[p] += y;
		return;
	}
	if(mid>=k){
		add(s,mid,k,y,p<<1);
	}
	else add(mid+1,t,k,y,p<<1|1);
	sum[p] = sum[p<<1]+sum[p<<1|1];
	return;
}
int found(int l,int r,int s,int t,int p){
	if(l<=s&&t<=r){
		return sum[p];
	}
	int ans = 0,mid = (s+t)>>1;
	if(l<=mid)ans += found(l,r,s,mid,p<<1);
	if(r>mid)ans += found(l,r,mid+1,t,p<<1|1);
	return ans ;
}int mx = -1;
int getans(int x){
	int l = 1,r = mx ;
	int sum = 0;
	while(l<r){
		int mid = (l+r)>>1;
		sum = found(1,mid,1,inf,1);
		if(sum>=x) r = mid;
		else l = mid + 1;
	}
	return l;
}
void solve(){
	scanf("%d",&n);
	while(n--){
		scanf("%s",op);
		if(op[0] == 'P'&&op[1] == 'o'){
			if(!tot)puts("Invalid");
			else {
				add(1,inf,now[tot--],-1,1);
				printf("%d\n",now[tot+1]);
			}
		}
		else if(op[1]=='u'){
			scanf("%d",&now[++tot]);
			mx = max(mx,now[tot]);
			add(1,inf,now[tot],1,1);
		}
		else {
			if(!tot){
				puts("Invalid");continue;
			}
			int t = (tot+1)>>1;
			printf("%d\n",getans(t));
		}
	}
	return;
}
int main(){
	solve();
	return 0;
}

這個建樹是我沒想到的,當初的想法想到了或許建樹時建1e5個節點,最初設為0;而類似桶排序的思路則是網上樹狀陣列題解提供給我的。這個題感覺是二分答案?二分答案類的題目現在有點不敢想,因為二分答案大多數範圍都很大,但其實仔細一想,就多了一個log複雜度,當正向想不出答案時,不妨利用逆向思維,在答案這個集合中進行考慮;