HDU 5919 Sequence II ( 主席树 )
题意 : 给出 N 个数、然后 M 个问询、问询格式是给出 ( L、R ) 然后需要根据规则变成新的 ( L'、R' ) [ 即此题强制在线了 ]、对于每个问询假设问询区间内有 X 个不同种类的数、每个数从左到右第一次出现的位置是 pos1、pos2... posX 然后要你给出 pos( (X+1)/2 ) 是多少
分析 :
主要就是查询区间内不同数的个数、区间内第 K 大
这两个都是主席树的基本功能、所以考虑使用主席树来做
从左到右对于每个前缀可持久化建树、然后对于每个区间问询先查出有多少个不同种类的数
然后再二分位置、把第 (X+1)/2 个找出来就是答案了。
但是二分会多出一个 log 、这里可以有避免二分的办法、需要一个技巧
就是从右往左建树、然后对于问询、就可以直接 log 地查询 K 大值了
#include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; struct NODE{ int sum, L, R; NODE(){}; NODE(int _sum, int _L, int _R): sum(_sum),L(_L),R(_R){}; }T[maxn*40]; int Tcnt = 0; int root[maxn]; int arr[maxn], N; int newNode(int sum, int L, int R) { T[++Tcnt] = NODE(sum, L, R); return Tcnt; } inline void Insert(int &root, int pre, int pos, int val, int L, int R) { root = newNode(T[pre].sum+val, T[pre].L, T[pre].R); if(L == R) return ; int M = L + ((R-L)>>1); if(pos <= M) Insert(T[root].L, T[pre].L, pos, val, L, M); else Insert(T[root].R, T[pre].R, pos, val, M+1, R); } int query(int rt, int pos, int L, int R) { if(L == R) return T[rt].sum; int ret = 0; int M = L + ((R-L)>>1); if(pos <= M) ret += query(T[rt].L, pos, L, M); else ret += query(T[rt].R, pos, M+1, R) + T[T[rt].L].sum; return ret; } int Kth(int K, int rt, int l, int r) { if(l == r) return l; int m = l + ((r-l)>>1); if(T[T[rt].L].sum >= K) return Kth(K, T[rt].L, l, m); else return Kth(K-T[T[rt].L].sum, T[rt].R, m+1, r); } map<int, int> mp; int main(void) { int nCase; scanf("%d", &nCase); for(int Case=1; Case<=nCase; Case++){ mp.clear(); printf("Case #%d:", Case); T[0] = NODE(0, 0, 0); root[N+1] = 0; Tcnt = 0; scanf("%d", &N); int Q; scanf("%d", &Q); for(int i=1; i<=N; i++) scanf("%d", &arr[i]); for(int i=N; i>=1; i--){ if(mp.count(arr[i])){ int tmpRoot; Insert(tmpRoot, root[i+1], i, 1, 1, N); Insert(root[i], tmpRoot, mp[arr[i]], -1, 1, N); }else Insert(root[i], root[i+1], i, 1, 1, N); mp[arr[i]] = i; } int PreAns = 0; while(Q--){ int l, r; scanf("%d %d", &l, &r); int A = (l+PreAns)%N+1; int B = (r+PreAns)%N+1; l = min(A, B); r = max(A, B); if(l > r) swap(l, r); int K = ( query(root[l], r, 1, N) + 1 ) / 2; //// 注释部分为二分写法 // int L = l, R = r, ans; // while(L <= R){ // int M = L + ((R-L)>>1); // if(query(root[l], M, 1, N) >= K) R = M-1, ans = M; // else L = M+1; // } // printf(" %d", PreAns = ans); printf(" %d", PreAns = Kth(K, root[l], 1, N)); }puts(""); } return 0; }