题目链接:点击打开链接
题目大意:给出n个数的初始序列,有两种操作,1 l v将第l个数换成v,2 l r k 问在区间[l,r]内的第k大是多少,并输出
经典的题目,但是树状数组+主席树(TLE)伸展树(MLE),听说他们用的块状链表,zhazha表示不会,后来补题,发现整体二分是一个好方法。
首先,这个整体二分是将数据范围和操作放到一起,不断二分数据的范围,可以得到有某些操作可以被完成,有某些操作不可以被完成。
例如:当前数据范围的区间是[l,r],那么mid = (l+r)/2 ,所有修改操作如果修改的数小于等于mid,那么就是可以被完成的,那么就修改数,否则就是不能被完成的。对于查询操作,如果查询到的范围内的数的个数x小于k,那么要查询的数是不在区间[1,5]内的,所以这个操作是不会再[l,mid]中被完成,将该操作的k改为k-x。否则就会在[l,mid]内被完成。按照数据范围向下二分得到[l,mid],[mid+1,r],将能被完成的操作放入左区间,不能完成的操作放入右区间。(注意放到同一区间的的操作的相对顺序是不能被改变的),这样最终的查询操作都会被确定到一个数值,这个数值就是要输出的值。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define maxn 300100 struct node{ int type, l, r, v , ans; }p[maxn]; int c[maxn], n, q, cnt, max1; int a[maxn]; int id1[maxn], id2[maxn]; void p_add(int type, int l,int r, int v) { p[cnt].type = type; p[cnt].l = l; p[cnt].r = r; p[cnt].v = v; id1[cnt] = cnt; cnt++; } int lowbit(int x) { return x & -x ; } void add(int i,int k) { while( i <= n ) { c[i] += k ; i += lowbit(i) ; } } int sum(int i) { int num = 0 ; while( i ) { num += c[i] ; i -= lowbit(i) ; } return num ; } void solve(int L, int R, int low, int high) { if( L > R ) return; if( low == high ) { while(L <= R) { if( p[ id1[L] ].type == 2 ) p[ id1[L] ].ans = low; L++; } return ; } int i, j, num, l = L, r = R; int mid = (low + high)/2; for(i = L; i <= R; i++) { j = id1[i]; if( p[j].type == 2 ) { num = sum(p[j].r) - sum(p[j].l-1); if( num < p[j].v ) { p[j].v -= num; id2[r--] = j; } else id2[l++] = j; } else { if( p[j].v <= mid ) { add(p[j].l,p[j].type); id2[l++] = j; } else id2[r--] = j; } } for(i = L; i <= R; i++) { j = id1[i]; if( p[j].type != 2 && p[j].v <= mid ) add(p[j].l,-p[j].type); } for(i = L; i < l; i++) id1[i] = id2[i]; for(r = R; i <= R; r--, i++) id1[i] = id2[r]; solve(L,l-1,low,mid); solve(l,R,mid+1,high); } int main() { int i, j, type, l, r, v; while( scanf("%d", &n) != EOF ) { memset(c,0,sizeof(c)); cnt = max1 = 0; for(i = 1; i <= n; i++) { scanf("%d", &a[i]); p_add(1, i, i, a[i]); max1 = max(max1, a[i]); } scanf("%d", &q); while( q-- ) { scanf("%d", &type); if( type == 1 ) { scanf("%d %d", &l, &v); p_add(-1, l, l, a[l]); a[l] = v; p_add(1, l, l, a[l]); max1 = max(max1, a[l]); } else { scanf("%d %d %d", &l, &r, &v); p_add(2, l, r, v); } } solve(0,cnt-1,0,max1) ; for(i = 0; i < cnt; i++) { if( p[i].type == 2 ) printf("%d\n", p[i].ans); } } return 0 ; }