1. 程式人生 > >HDU 640 Taotao Picks Apples & FJUT3592 做完其他題後才能做的題(線段樹)題解

HDU 640 Taotao Picks Apples & FJUT3592 做完其他題後才能做的題(線段樹)題解

題意(FJUT翻譯HDU):

錢陶陶家門前有一棵蘋果樹。 秋天來了,樹上的n個蘋果成熟了,淘淘會去採摘這些蘋果。

到園子裡摘蘋果時,淘淘將這些蘋果從第一個蘋果掃到最後一個。 如果當前的蘋果是第一個蘋果,或者它嚴格高於之前選擇的蘋果,那麼淘淘將採摘這個蘋果; 否則,他不會選擇。

題目來了:已知這些蘋果的高度為h1,h2,⋯,hn,您需要回答一些獨立的查詢。 每個查詢是兩個整數p,q,表示如果第p個蘋果的高度修改為q,詢問當前淘淘將摘到的蘋果的數量。 你能解決這個問題嗎?

思路:pre表示1~i的最大連續答案,tail代表i~n的最大連續答案。最終答案為p前,p,p後三部分的貢獻和。

預處理pre和tail。tail從後往前預處理,用二分(線段樹)查詢第一個比i位置大的數。

然後每次找p前最大值下標preMax,ans += pre[preMax],為p前貢獻。

ans += q > a[preMax],為p的貢獻。

查詢p後第一個大於前面所有數的值的下標tailMax,ans += tail[tailMax],為p後貢獻。

三者貢獻和為總貢獻。

二分查詢p後第一個大於前面所有數的值的下標一頓好寫啊(微笑

 

思考了半天發現是線段樹寫錯了。顯然(?)只有在L <= l && R >= r(就是說當前區間是查詢區間子集)才能直接剪枝選擇左兒子或者右兒子。

程式碼:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = 100000 + 10
; const int seed = 131; const ll MOD = 1e9 + 7; const ll INF = 0x3f3f3f3f; int n; ll a[maxn]; ll pre[maxn], tail[maxn], Max[maxn << 2]; //1~i最多遞增,i~n最多遞增 void build(int l, int r, int rt){ if(l == r){ Max[rt] = a[l]; return; } int m = (l + r) >> 1; build(l, m, rt << 1); build(m + 1, r, rt << 1 | 1); Max[rt] = max(Max[rt << 1], Max[rt << 1 | 1]); } int tailMax, preMax; void queryPre(int l, int r, int L, int R, int rt){ if(R == 0) return; if(l == r){ if(a[l] > a[preMax]) preMax = l; return; } int m = (l + r) >> 1; if(L <= l && R >= r){ if(Max[rt << 1] > Max[rt << 1 | 1]) queryPre(l, m, L, R, rt << 1); else queryPre(m + 1, r, L, R, rt << 1 | 1); return; } if(L <= m) queryPre(l, m, L, R, rt << 1); if(R > m) queryPre(m + 1, r, L, R, rt << 1 | 1); } void queryTail(int l, int r, int L, int R, int rt, ll v){ if(L == n + 1) return; if(l == r){ if(Max[rt] > v) tailMax = min(l, tailMax); return; } int m = (l + r) >> 1; if(L <= l && R >= r){ if(Max[rt << 1] > v){ queryTail(l, m, L, R, rt << 1, v); } else if(Max[rt << 1 | 1] > v){ queryTail(m + 1, r, L, R, rt << 1 | 1, v); } return; } if(Max[rt << 1] > v && L <= m){ queryTail(l, m, L, R, rt << 1, v); } if(Max[rt << 1 | 1] > v && R > m){ queryTail(m + 1, r, L, R, rt << 1 | 1, v); } } int main(){ int m, T; scanf("%d", &T); while(T--){ scanf("%d%d", &n ,&m); ll u = 0; pre[0] = 0; a[0] = 0; for(int i = 1; i <= n; i++){ scanf("%lld", &a[i]); if(a[i] > u){ u = a[i]; pre[i] = pre[i - 1] + 1; } else{ pre[i] = pre[i - 1]; } } build(1, n, 1); for(int i = n; i >= 1; i--){ tailMax = INF; queryTail(1, n, i + 1, n, 1, a[i]); if(tailMax == INF) tail[i] = 1; else tail[i] = 1 + tail[tailMax]; } while(m--){ int p; ll q; scanf("%d%lld", &p, &q); ll ans = 0; preMax = 0; queryPre(1, n, 1, p - 1, 1); ans += pre[preMax]; if(q > a[preMax]){ ans += 1; } tailMax = INF; queryTail(1, n, p + 1, n, 1, max(a[preMax], q)); if(tailMax != INF) ans += tail[tailMax]; printf("%lld\n", ans); } } return 0; }