hdu 6703 array 权值线段树
题目读了半天没读懂......
给你一个长度为n的排列a。两种操作,每次把里面的某个数增加10,000,000,或者询问不等于a1-ar,且不小于k的最小的数是多少。k不超过n。所以答案必定不超过n+1。
我们考虑对原序列做权值线段树,下标为原序列的值,值为原序列的下标。
先考虑没有修改,那么不小于k就是只从线段树下标为[k + 1,n + 1]的范围找答案。不等于a1-ar就是线段树的值(原序列下标)要大于r。要最小的数,就是能往左子树找就往左子树找。
考虑修改,某个数ai增加了10,000,000,则意味着,原先ai这个数,再也不会被a1-ar这个条件限制了,因为a是一个排列,ai独此一份,现在被改成了个很大的数。所以干脆把线段树下标ai对应的值改成n+1,这样子,就再也不会受a1-ar这个条件限制了。
复杂度是mlogn,直观感受下就是,不再范围内的部分,发现出界会立马停止。在范围内的部分,只有一条会递归到底,一共两条,所是logn。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int MAXN = 101000; 7 int n,m,T,vec[MAXN],a[MAXN]; 8 struct segtree 9 { 10 int maxn[4 * MAXN]; 11 void build(int k,int l,int r,int *vec) 12 { 13 if (l == r) 14 { 15 maxn[k] = vec[l]; 16 return; 17 } 18 int mid = l + r >> 1; 19 build(k << 1,l,mid,vec); 20 build(k << 1 | 1,mid + 1,r,vec); 21 maxn[k] = max(maxn[k << 1],maxn[k << 1 | 1]); 22 } 23 void change(int k,int l,int r,int x) 24 { 25 if (l == r) 26 { 27 maxn[k] = n + 1; 28 return; 29 } 30 int mid = l + r >> 1; 31 if (x <= mid) 32 change(k << 1,l,mid,x); 33 else 34 change(k << 1 | 1,mid + 1,r,x); 35 maxn[k] = max(maxn[k << 1],maxn[k << 1 | 1]); 36 } 37 int query(int k,int l,int r,int x,int y) 38 { 39 if (r < y) 40 return -1; 41 if (l == r) 42 return l; 43 int mid = l + r >> 1,res = -1; 44 if (maxn[k << 1] > x) 45 res = query(k << 1,l,mid,x,y); 46 if (res == -1) 47 if (maxn[k << 1 | 1] > x) 48 res = query(k << 1 | 1,mid + 1,r,x,y); 49 return res; 50 } 51 } st; 52 int main() 53 { 54 for (scanf("%d",&T);T != 0;T--) 55 { 56 57 int lst = 0; 58 scanf("%d%d",&n,&m); 59 for (int i = 1;i <= n;i++) 60 { 61 scanf("%d",&a[i]); 62 vec[a[i]] = i; 63 } 64 vec[n + 1] = n + 1; 65 st.build(1,1,n + 1,vec); 66 int tx,ty,opt; 67 for (int i = 1;i <= m;i++) 68 { 69 scanf("%d",&opt); 70 if (opt == 1) 71 { 72 scanf("%d",&tx); 73 tx ^= lst; 74 st.change(1,1,n + 1,a[tx]); 75 }else 76 { 77 scanf("%d%d",&tx,&ty); 78 tx ^= lst; 79 ty ^= lst; 80 lst = st.query(1,1,n + 1,tx,ty); 81 printf("%d\n",lst); 82 } 83 } 84 } 85 }
心之所动 且就随缘去吧