特殊堆疊(線段樹題解)
阿新 • • 發佈:2020-11-26
堆疊是一種經典的後進先出的線性結構,相關的操作主要有“入棧”(在堆疊頂插入一個元素)和“出棧”(將棧頂元素返回並從堆疊中刪除)。本題要求你實現另一個附加的操作:“取中值”——即返回所有堆疊中元素鍵值的中值。給定 N 個元素,如果 N 是偶數,則中值定義為第 N/2 小元;若是奇數,則為第 (N+1)/2 小元。
輸入格式:
輸入的第一行是正整數 N()。隨後 N 行,每行給出一句指令,為以下 3 種之一:
Push key
Pop
PeekMedian
其中 key
是不超過 的正整數;Push
表示“入棧”;Pop
表示“出棧”;PeekMedian
輸出格式:
對每個 Push
操作,將 key
插入堆疊,無需輸出;對每個 Pop
或 PeekMedian
操作,在一行中輸出相應的返回值。若操作非法,則對應輸出 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複雜度,當正向想不出答案時,不妨利用逆向思維,在答案這個集合中進行考慮;