HDU 4417 —— Super Mario(樹狀陣列,離散化,離線處理)
阿新 • • 發佈:2019-02-19
意思比較簡單,就是給N個數(下標從0開始),然後q個詢問,三個引數,L,R,H,詢問序列中在【L,R】這個區間上小於等於H的個數。
挺綜合的一道題目。因為數字最多100000個,數值最多達10^9,所以首先要對數值進行離散化。
像這種區間查詢問題,用S(X,H)表示從0開始到X,小於等於H的個數,那麼每個查詢就可以轉化成S(R,H)-S(L-1,H),這個直接寫成樹狀陣列或線段樹是比較困難的。
轉成離線處理會比較簡單。具體來說就是把每個查詢拆成兩個事件,一個對應L,一個對應R,一共有2q個事件,將所有事件從左到右排序,如果對應的位置相同,那麼對應左端點的應該優先在前。
然後就是遍歷0~N-1,遇到事件左端點的,用k表示H在離散化之後對應的下標,減掉SUM(k),把a[i]對應的位置新增到樹狀陣列中,遇到右端點,就加上SUM(k)。
這樣,由於到了某個i的時候,後面的數值還沒加進來,所以對當前值沒有影響,求出來的SUM自然就是對應0到當前端點的值。
最後在一口氣將所有答案輸出即可。
具體還是見程式碼吧。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100000 #define M 200010 struct Event{ int type;//事件型別,0表示左端點,1表示右端點 int id;//事件對應的查詢的編號 int x;//事件對應的端點 int v;//事件對應的H值 bool operator < (const Event& tmp)const{ if(x==tmp.x) return type<tmp.type; return x<tmp.x; } }ev[M]; inline void in(int& num){ char c=getchar(); num=0; while(c<48 || c>57) c=getchar(); while(c>=48 && c<=57){ num = num*10+c-48; c = getchar(); } } int t, ct, n, m, q, p, l, r, x, i, j, k; int a[N], b[M], s[M], ans[N]; void add(int v){ for(;v<=m;v+=(v&(-v))) s[v]++; } int sum(int v){ int res=0; for(;v;v-=(v&(-v))) res+=s[v]; return res; } int main(){ in(t); for(ct=1; ct<=t; ct++){ in(n); in(q); m=0; for(i=0; i<n; i++){ in(a[i]); b[m++]=a[i]; } for(i=0; i<q; i++){ in(l); in(r); in(x); ev[i<<1].type=0; ev[i<<1].id=i; ev[i<<1].x=l; ev[i<<1].v=x; j = (i<<1)|1; ev[j].type=1; ev[j].id=i; ev[j].x=r; ev[j].v=x; b[m++] = x; } p = q<<1; sort(ev, ev+p); sort(b, b+m); m = unique(b, b+m)-b; memset(s,0,sizeof(s)); j=0; for(i=0; i<n; i++){ //先處理掉左端點 while(j<p && ev[j].type==0 && ev[j].x==i){ k = lower_bound(b, b+m, ev[j].v)-b+1; ans[ev[j].id] = 0 - sum(k); j++; } //更新當前的a[i] k = lower_bound(b, b+m, a[i])-b+1; add(k); //處理右端點 while(j<p && ev[j].type==1 && ev[j].x==i){ k = lower_bound(b, b+m, ev[j].v)-b+1; ans[ev[j].id] += sum(k); j++; } } printf("Case %d:\n", ct); for(i=0; i<q; i++) printf("%d\n", ans[i]); } return 0; }