何为可持久化线段树
我们通常建立的线段树是可以支持修改操作更新操作和查询操作的,却不支持保存你更改每个操作的历史版本。而可持久化线段树就是可以保存各个历史版本的线段树。
最朴素的保存各个历史版本的线段树就是将每次更改后的线段树都保存下来,但是这样操作有个很严重的问题:太吃空间了。你n次修改就要保存n+1棵树,数据量一大,你所需要的空间就直线上升。
那有没有什么好解决这个问题的方法呢?有没有可以节省空间的方法呢?这时很容易想到,当前修改的线段树,除了修改过的结点以外,其他的结点跟上一棵线段树是一模一样的。那我们就可以通过共用这些相同的节点,来达到节省空间的目的,这就是可持久化线段树。
建立一棵可持久化线段树
既然是可持久化线段树,辣么对于最初的线段树的每个结点的数据结构我们不仅要保留线段树所包含区间端点l,r,值和标记val,tag(多个tag同样),还要增加左子树和右子树的指针lt,rt。
ACM一般采用数组模拟链表方式,而历史化版本由于除了普通update更新结点,也有可能pushdown或pushup才更新结点,所以一般新建的结点即使作废了,你也不好删除,所以经常是不删除多余的历史版本的。
跟线段树一样都是那几个操作,init()建立,update()更新,query()查询,pushdown()向下更新和pushup()向上更新。还需要记录每次更改的根结点,用一个root[](rt[])数组来存各个历史版本的根结点。不同之处在于init()和update()以及pushdown()、pushup()作为更新的手段(init的话是最初当做一棵空树去更新),需要每次更新都创建一个和原位置相同的结点,然后将父节点相应的孩子指针指向该结点,然后再修改。我们可以在更新的时候巧妙使用&,例如update(segtree[i].lt,l,r,val) 而update的函数变量表为update(int &i,int l,int r,int val), 函数体开头就是segtree[++cnt]=segtree[i]; i=cnt;这样一个巧妙的操作,就顺便把父节点的指针也指向了该结点,也就能将可持久化线段树的写法变得和普通线段树相差无几。
每次更新新结点能构成一个小的简单的二叉树。
然后其他操作就跟普通线段树一样了。
下面直接上题练手,看代码:
习题一
To the moon
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 5016 Accepted Submission(s): 1129
To The Moon is a independent game released in November 2011, it is a role-playing adventure game powered by RPG Maker.
The premise of To The Moon is based around a technology that allows us to permanently reconstruct the memory on dying man. In this problem, we'll give you a chance, to implement the logic behind the scene.
You‘ve been given N integers A[1], A[2],..., A[N]. On these integers, you need to implement the following operations:
1. C l r d: Adding a constant d for every {Ai | l <= i <= r}, and increase the time stamp by 1, this is the only operation that will cause the time stamp increase.
2. Q l r: Querying the current sum of {Ai | l <= i <= r}.
3. H l r t: Querying a history sum of {Ai | l <= i <= r} in time t.
4. B t: Back to time t. And once you decide return to a past, you can never be access to a forward edition anymore.
.. N, M ≤ 105, |A[i]| ≤ 109, 1 ≤ l ≤ r ≤ N, |d| ≤ 104 .. the system start from time 0, and the first modification is in time 1, t ≥ 0, and won't introduce you to a future state.
A1 A2 ... An
... (here following the m operations. )
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define clr(x) memset(x,0,sizeof(x)) 5 #define LL long long 6 using namespace std; 7 struct segtree 8 { 9 int lt,rt,begin,end; 10 LL sum,tag; 11 }ret[800010]; 12 int l,r,rt[100010],a[100010],cnt,now,n,m,ct; 13 char op; 14 void init(int &i,int lt,int rt); 15 void update(int &i,int l,int r,int val); 16 LL query(int i,int l,int r); 17 int max(int a,int b) 18 { 19 return a>b?a:b; 20 } 21 int min(int a,int b) 22 { 23 return a<b?a:b; 24 } 25 int main() 26 { 27 bool b=0; 28 while(scanf("%d%d",&n,&m)!=EOF) 29 { 30 if(b) 31 printf(" "); 32 else 33 b=1; 34 now=cnt=0; 35 for(int i=1;i<=n;i++) 36 scanf("%d",&a[i]); 37 init(rt[now],1,n); 38 for(int i=1;i<=m;i++) 39 { 40 scanf(" %c",&op); 41 if(op=='C') 42 { 43 now++; 44 scanf("%d%d%d",&l,&r,&ct); 45 update(rt[now]=rt[now-1],l,r,ct); 46 } 47 if(op=='Q') 48 { 49 scanf("%d%d",&l,&r); 50 printf("%lld ",query(rt[now],l,r)); 51 } 52 if(op=='H') 53 { 54 scanf("%d%d%d",&l,&r,&ct); 55 printf("%lld ",query(rt[ct],l,r)); 56 } 57 if(op=='B') 58 { 59 scanf("%d",&now); 60 cnt=rt[now+1]-1; 61 } 62 } 63 } 64 return 0; 65 } 66 void init(int &i,int lt,int rt) 67 { 68 ret[++cnt]=ret[i]; 69 i=cnt; 70 ret[i].begin=lt; 71 ret[i].end=rt; 72 if(lt==rt) 73 { 74 ret[i].sum=a[lt]; 75 return; 76 } 77 int mid=(lt+rt)>>1; 78 init(ret[i].lt,lt,mid); 79 init(ret[i].rt,mid+1,rt); 80 ret[i].sum=ret[ret[i].lt].sum+ret[ret[i].rt].sum; 81 return ; 82 } 83 void update(int &i,int l,int r,int val) 84 { 85 ret[++cnt]=ret[i]; 86 i=cnt; 87 ret[i].sum+=(min(r,ret[i].end)-max(l,ret[i].begin)+1)*val; 88 if(ret[i].begin>=l && ret[i].end<=r) 89 { 90 if(ret[i].begin!=ret[i].end) 91 ret[i].tag+=val; 92 return ; 93 } 94 int mid=(ret[i].begin+ret[i].end)>>1; 95 if(r<=mid) 96 update(ret[i].lt,l,r,val); 97 else if(l>mid) 98 update(ret[i].rt,l,r,val); 99 else 100 { 101 update(ret[i].lt,l,r,val); 102 update(ret[i].rt,l,r,val); 103 } 104 105 } 106 LL query(int i,int l,int r) 107 { 108 if(ret[i].begin>=l && ret[i].end<=r) 109 return ret[i].sum; 110 LL tmp=(min(r,ret[i].end)-max(l,ret[i].begin)+1)*ret[i].tag; 111 int mid=(ret[i].begin+ret[i].end)>>1; 112 if(r<=mid) 113 tmp+=query(ret[i].lt,l,r); 114 else if(l>mid) 115 tmp+=query(ret[i].rt,l,r); 116 else 117 tmp+=query(ret[i].lt,l,r)+query(ret[i].rt,l,r); 118 return tmp; 119 }
辣么这东西除了存储历史版本还能解决什么问题呢?
那就是!区间第k大问题!
区间第k大问题
即求解一个区间内第k大的数的问题。该问题又分为分为带修改以及不带修改的区间第k大问题。
可持久化线段树求解不带修改的区间第k大问题
对于该问题有人直接复制该区间sort解决,也有用ST去筛选去除的,这两种方法对于大量询问是十分慢的。这里介绍用可持久化线段树解决该问题。
首先是查询前的准备工作。
对于该问题,我们可以先复制待查询数组num,形成mirror数组a,利用sort()和unique()对该数组排序去重,然后依照这个数组建立一棵空的权值线段树,该权值线段树结点i的权值num为该区间l~r(排好序并去重从小到大的a的区间l~r)内所有数字的数量,起初为0.对于叶子结点i还要建立多一个val成员,来记录该点对应的数字a[ret[i].l]。其实这里这么做就是将a数组区间所有数离散化了。而每个结点存储的值num即为该区间的数字数量。
然后将待查询数组的数字从头到尾一个一个插入权值线段树中。每次插入都对应一个新的root,所以第i次插入就有一个root[i]。root[i]对应从一开始到现在所有数,也就是[1,r]所有数形成的权值线段树。而更改结点就是将其num++。
好了准备工作做完了,接下来怎么实现查询呢?
你有没有发觉,我们这样建立的权值线段树是可以相减的!相减的结果(root[j]-root[i]的线段树)对应的就是j到i+1之间的所有操作形成的线段树!换句话说对于区间[l,r],他的权值线段树对应root[r]-root[l-1]的线段树!这棵线段树存储了[l,r]区间内所有的数并从小到大排列。因此最重要的问题:快速将[l,r]内的所有数排序这个问题,化为可持久化线段树后,相当于通过预处理解决了。接下来就有些类似二分查找的思想进行查找。查询到结点i(i非叶子结点)时,若root[r]-root[l-1]对应的线段树(以下省略,直接当做一棵线段树)的左子树的数字数量ret[ret[i].lc].num>=k,则说明第k大的数在左区间内,查询左区间(左孩子),反之在右区间,查询右区间。直到到达一个叶子结点,返回val即为该其区间第k大数。
下面是两道模板题:
习题二
K-th Number
Description
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
Input
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output
Sample Input
7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3
Sample Output
5 6 3
Hint
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<vector> 6 #include<algorithm> 7 #define clr(x) memset(x,0,sizeof(x)) 8 using namespace std; 9 vector<int> bucket[500]; 10 int a[200010],num[200010]; 11 int L[100010],R[100010],K[100010]; 12 const int b=1000; 13 int main() 14 { 15 int n,m; 16 scanf("%d%d",&n,&m); 17 for(int i=1;i<=n;i++) 18 { 19 scanf("%d",&a[i]); 20 num[i]=a[i]; 21 bucket[i/b].push_back(a[i]); 22 } 23 for(int i=1;i<=m;i++) 24 { 25 scanf("%d%d%d",&L[i],&R[i],&K[i]); 26 } 27 sort(num+1,num+n+1); 28 29 for(int i=0;i<=n/b;i++) 30 { 31 sort(bucket[i].begin(),bucket[i].end()); 32 } 33 for(int i=1;i<=m;i++) 34 { 35 int l=L[i]; 36 int r=R[i]; 37 int k=K[i]; 38 int ld=0; 39 int rd=n; 40 while(rd-ld>1) 41 { 42 int mid=(ld+rd)/2; 43 // printf("%d %d",ld,rd); 44 45 int x=num[mid]; 46 int lt=l; 47 int rt=r+1; 48 int sum=0; 49 while(lt<rt && lt%b!=0) if(a[lt++]<=x) sum++; 50 // printf(" %d %d",sum,lt); 51 while(lt<rt && rt%b!=0) if(a[--rt]<=x) sum++; 52 // printf(" %d %d",sum,rt); 53 for(int i=lt/b;i*b+b<=rt;i++) 54 { 55 sum+=upper_bound(bucket[i].begin(),bucket[i].end(),x) -bucket[i].begin(); 56 // printf(" %d %d",sum,lt); 57 58 } 59 if(sum<k) 60 ld=mid; 61 else 62 rd=mid; 63 // printf(" %d %d ",sum,x); 64 } 65 printf("%d ",num[rd]); 66 } 67 return 0; 68 }
主席树代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define clr(x) memset(x,0,sizeof(x)) 6 using namespace std; 7 struct segtree 8 { 9 int lc,rc,l,r,num,val; 10 }ret[2000010]; 11 void init(int &i,int l,int r); 12 int find(int num,int l,int r); 13 void update(int &i,int pos); 14 int query(int i,int j,int k); 15 int num[100010],a[100010],n,m,root[100010],rnu,cnt,now; 16 int main() 17 { 18 clr(root); 19 clr(a); 20 clr(ret); 21 int lt,rt,k; 22 while(scanf("%d%d",&n,&m)!=EOF) 23 { 24 for(int i=1;i<=n;i++) 25 scanf("%d",&num[i]); 26 memcpy(a,num,sizeof(num)); 27 sort(a+1,a+(n+1)); 28 rnu=unique(a+1,a+(n+1))-a; 29 rnu--; 30 cnt=now=0; 31 /** for(int i=0;i<=n;i++) 32 printf("%d ",a[i]); 33 printf(" %d ",rnu);*/ 34 init(root[0],1,rnu); 35 for(int i=1;i<=n;i++) 36 { 37 now++; 38 update(root[now]=root[now-1],find(num[i],1,rnu)); 39 } 40 for(int i=1;i<=m;i++) 41 { 42 scanf("%d%d%d",<,&rt,&k); 43 printf("%d ",query(root[lt-1],root[rt],k)); 44 } 45 } 46 return 0; 47 } 48 void init(int &i,int l,int r) 49 { 50 ret[++cnt]=ret[i]; 51 i=cnt; 52 ret[i].l=l; 53 ret[i].r=r; 54 if(l==r) 55 { 56 ret[i].val=a[l]; 57 return ; 58 } 59 int mid=(l+r)>>1; 60 init(ret[i].lc,l,mid); 61 init(ret[i].rc,mid+1,r); 62 return ; 63 } 64 int find(int num,int l,int r) 65 { 66 int mid=(l+r)>>1; 67 if(a[mid]>num && mid>l) 68 return find(num,l,mid-1); 69 if(a[mid]<num && mid<r) 70 return find(num,mid+1,r); 71 return mid; 72 } 73 void update(int &i,int pos) 74 { 75 ret[++cnt]=ret[i]; 76 i=cnt; 77 ret[i].num++; 78 int mid=(ret[i].l+ret[i].r)>>1; 79 if(mid>=pos && ret[i].l!=ret[i].r) 80 update(ret[i].lc,pos); 81 if(mid<pos && ret[i].l!=ret[i].r) 82 update(ret[i].rc,pos); 83 return ; 84 } 85 int query(int i,int j,int k) 86 { 87 if(ret[i].l==ret[i].r) 88 return ret[i].val; 89 int size=ret[ret[j].lc].num-ret[ret[i].lc].num; 90 if(size>=k) 91 return query(ret[i].lc,ret[j].lc,k); 92 else 93 return query(ret[i].rc,ret[j].rc,k-size); 94 }
后来想了想,离散化后不需要定位,直接把该数字插入就好了,可以省去find()函数(或lower_bound()),相应的你将每个结点的区间[l,r]右端点所对应的a数组的值作为其val值,更新查找只需要和该val比较就行。
优化后又做了道模板题,如下:
习题三
Super Mario
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6137 Accepted Submission(s): 2667
For each test data:
The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.
Next line contains n integers, the height of each brick, the range is [0, 1000000000].
Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define clr(x) memset(x,0,sizeof(x)) 6 using namespace std; 7 struct segtree 8 { 9 int l,r,val,num,lc,rc; 10 }ret[1000010]; 11 int a[100010],num[100010],n,m,now,root[100010],cnt,rnu,l,r,k; 12 void init(int &i,int l,int r); 13 void update(int &i,int num); 14 int query(int i,int j,int k); 15 int main() 16 { 17 int T; 18 scanf("%d",&T); 19 clr(ret); 20 for(int tt=1;tt<=T;tt++) 21 { 22 printf("Case %d: ",tt); 23 scanf("%d%d",&n,&m); 24 for(int i=1;i<=n;i++) 25 scanf("%d",&num[i]); 26 memcpy(a,num,sizeof(num)); 27 sort(a+1,a+(n+1)); 28 rnu=unique(a+1,a+(n+1))-a; 29 rnu--; 30 cnt=now=0; 31 init(root[0],1,rnu); 32 for(int i=1;i<=n;i++) 33 { 34 now++; 35 update(root[now]=root[now-1],num[i]); 36 } 37 for(int i=1;i<=m;i++) 38 { 39 scanf("%d%d%d",&l,&r,&k); 40 printf("%d ",query(root[l],root[r+1],k)); 41 } 42 } 43 return 0; 44 } 45 void init(int &i,int l,int r) 46 { 47 ret[++cnt]=ret[i]; 48 i=cnt; 49 ret[i].l=l; 50 ret[i].r=r; 51 int mid=(l+r)>>1; 52 ret[i].val=a[r]; 53 if(l==r) 54 { 55 return ; 56 } 57 init(ret[i].lc,l,mid); 58 init(ret[i].rc,mid+1,r); 59 return ; 60 } 61 void update(int &i,int num) 62 { 63 ret[++cnt]=ret[i]; 64 i=cnt; 65 ret[i].num++; 66 if(ret[i].l==ret[i].r) 67 return; 68 int mid=(ret[i].l+ret[i].r)>>1; 69 if(a[mid]>=num) 70 update(ret[i].lc,num); 71 else 72 update(ret[i].rc,num); 73 return ; 74 } 75 int query(int i,int j,int k) 76 { 77 if(ret[i].val<=k) 78 return ret[j].num-ret[i].num; 79 else 80 { 81 int mid=(ret[i].l+ret[i].r)>>1; 82 if(a[mid+1]<=k) 83 return query(ret[i].lc,ret[j].lc,k)+query(ret[i].rc,ret[j].rc,k); 84 else 85 return query(ret[i].lc,ret[j].lc,k); 86 } 87 }
树套树求解带修改的区间第k大问题
做这类题,我们需要一个树套树板子,或者手打一个板子(如果你够强时间够多)。常见做法有线段树套平衡树,树状数组套可持久化线段树,整体二分。在这里我们讲树状数组套可持久化线段树。
我们先来看看不套树的情况。我们若是对某个点l进行修改,那么版本l及之后的版本都需要修改,这样增加的修改时间是O(n), 显然很多题目这样的时间试过不了的。
我们发觉,对于一个历史版本l,他更倾向于一个从1到l的单个结点树的前缀和,这样我们就可以考虑将历史版本再细分,以树状数组的形式来对该历史版本求和,当然存入的时候也以树状数组的形式存入。
我们还需要一个root数组,这时候root数组表示的意义,就跟树状数组里一样了,他表示的是对应历史版本区间[l,r]的结点树构成的一棵线段树。这样我们在存入的时候虽然要*O(logn)的时间以树状数组的形式存入,但是我们却可以在O(logn)的时间内修改点值,而查询的时间较之前要*O(logn),总体上来说对于多修改的第k大能尽量缩少时间。
因为是历史化版本,所以废弃的空间我们是无法收回的,约会浪费和原来线段树相同结点数的空间。
下面两道例题,第一道,这样的树套树效率还是显得低下,可以考虑用其他方法来做,把代码贴出来只是为了安慰自己的第一个树套树代码。。也能算模板吧。。第二个树套树能过,加油做吧~
习题四
CRB and Queries
Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 3249 Accepted Submission(s): 834
Boy i has his coding skill Ai.
CRB wants to know who has the suitable coding skill.
So you should treat the following two types of queries.
Query 1: 1 l v
The coding skill of Boy l has changed to v.
Query 2: 2 l r k
This is a report query which asks the k-th smallest value of coding skill between Boy l and Boy r(both inclusive).
The first line contains a single integer N.
Next line contains N space separated integers A1, A2, …, AN, where Ai denotes initial coding skill of Boy i.
Next line contains a single integer Q representing the number of queries.
Next Q lines contain queries which can be any of the two types.
1 ≤ N, Q ≤ 105
1 ≤ Ai, v ≤ 109
1 ≤ l ≤ r ≤ N
1 ≤ k ≤ r – l + 1
树状数组套可持久化线段树代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define clr(x) memset(x,0,sizeof(x)) 6 using namespace std; 7 struct segtree 8 { 9 int l,r,val,num,lc,rc; 10 }ret[1000010];//可持久化权值线段树,val为右端点对应的数字 11 struct node 12 { 13 int tmp,l,r,k,pos,v; 14 }que[100010];//操作数组,存储所有m个操作,该题目离线处理 15 int a[200010],num[100010],cnt,root[100010],n,m,l,r,k,tr[100010],rnu,op;//a为所有出现的数字离散化后的排序数组,num为初始各个位置的数组,root为每个树状数组代表区间对应的数字的根节点。tr为并行处理求[l,r]和时需要用到的数组 16 void init(int &i,int l,int r); 17 void add(int i,int num,int x); 18 void update(int &i,int num,int val); 19 int sum(int i,int j,int k); 20 int query(int i,int j,int k); 21 int getsum(int l); 22 int main() 23 { 24 while(scanf("%d",&n)!=EOF) 25 { 26 //初始化 27 clr(ret); 28 clr(tr); 29 clr(que); 30 clr(root); 31 for(int i=1;i<=n;i++) 32 { 33 scanf("%d",&num[i]); 34 } 35 memcpy(a,num,sizeof(num));//复制a数组作为离散化数组 36 cnt=0; 37 rnu=n; 38 scanf("%d",&m); 39 //将所有操作读入以后离线处理,很多都是这么做的 40 for(int i=1;i<=m;i++) 41 { 42 scanf("%d",&op); 43 if(op==1) 44 { 45 que[i].tmp=1; 46 scanf("%d%d",&que[i].pos,&que[i].v); 47 a[++rnu]=que[i].v; //将有可能出现的数字加入a数组中 48 } 49 else 50 { 51 que[i].tmp=2; 52 scanf("%d%d%d",&que[i].l,&que[i].r,&que[i].k); 53 } 54 } 55 //a排序去重 56 sort(a+1,a+rnu+1); 57 rnu=unique(a+1,a+rnu+1)-a-1; 58 // printf("-1 "); 59 init(root[0],1,rnu);//建初始空树 60 for(int i=1;i<=n;i++) 61 root[i]=root[0];//每个区间最初的状态,相当于一维树状数组的0初状态 62 // printf("1 "); 63 for(int i=1;i<=n;i++) 64 add(i,num[i],1);//以树状数组分割的形方式加入每个数字 65 // printf("2 "); 66 for(int i=1;i<=m;i++) 67 if(que[i].tmp==1) 68 { 69 add(que[i].pos,num[que[i].pos],-1);//删除原来的数字 70 add(que[i].pos,que[i].v,1);//加入修改后的数字 71 num[que[i].pos]=que[i].v; 72 } 73 else 74 { 75 printf("%d ",sum(que[i].l-1,que[i].r,que[i].k));//输出第K大数字 76 } 77 } 78 return 0; 79 } 80 void init(int &i,int l,int r) 81 { 82 ret[++cnt]=ret[i]; 83 i=cnt; 84 // printf("%d %d ",l,r); 85 ret[i].l=l; 86 ret[i].r=r; 87 ret[i].val=a[r]; 88 if(l==r) 89 return ; 90 int mid=(l+r)>>1; 91 init(ret[i].lc,l,mid); 92 init(ret[i].rc,mid+1,r); 93 return ; 94 } 95 void add(int i,int num,int x) 96 { 97 98 for(;i<=n;i+=i&-i) 99 update(root[i],num,x);//每个需要的版本加入该元素 100 return ; 101 } 102 void update(int &i,int num,int val) 103 { 104 ret[++cnt]=ret[i]; 105 i=cnt; 106 // printf("%d %d %d %d ",ret[i].l,ret[i].r,ret[i].val,ret[i].num); 107 ret[i].num+=val; 108 if(ret[i].l==ret[i].r) 109 return ; 110 if(num<=ret[ret[i].lc].val) 111 update(ret[i].lc,num,val); 112 else 113 update(ret[i].rc,num,val); 114 return ; 115 } 116 int sum(int i,int j,int k) 117 { 118 //先将需用到的区间的标号的根结点更新至tr数组 119 for(int l=i;l>0;l-=l&-l) 120 tr[l]=root[l]; 121 for(int r=j;r>0;r-=r&-r) 122 tr[r]=root[r]; 123 // printf("k:%d ",k); 124 return a[query(i,j,k)];//查询得出位置,返回该位置数字 125 } 126 int query(int i,int j,int k) 127 { 128 if(ret[tr[i]].l==ret[tr[i]].r) 129 return ret[tr[i]].l; 130 int lt=ret[tr[i]].l,rt=ret[tr[i]].r; 131 //多棵树并行走用getsum求某个前缀和线段树的和,相当于求玩前缀和后的然后右版本j-左版本i,跟两颗时是一样的 132 int sum=getsum(j)-getsum(i); 133 // printf("sum:%d %d %d %d ",sum,k,ret[tr[i]].l,ret[tr[i]].r); 134 if(sum>=k) 135 { 136 //所有树往左孩子走 137 for(int l=i;l>0;l-=l&-l) 138 tr[l]=ret[tr[l]].lc; 139 for(int r=j;r>0;r-=r&-r) 140 if(ret[tr[r]].l==lt && ret[tr[r]].r==rt)//防止多次对一个区间偏移 141 tr[r]=ret[tr[r]].lc; 142 return query(i,j,k); 143 } 144 else 145 { 146 //所有树往左孩子走 147 for(int l=i;l>0;l-=l&-l) 148 tr[l]=ret[tr[l]].rc; 149 for(int r=j;r>0;r-=r&-r) 150 if(ret[tr[r]].l==lt && ret[tr[r]].r==rt) 151 tr[r]=ret[tr[r]].rc; 152 return query(i,j,k-sum); 153 } 154 } 155 int getsum(int l)//求多棵树左孩子的权值。 156 { 157 int ans=0; 158 for(int i=l;i>0;i-=i&-i) ans+=ret[ret[tr[i]].lc].num; 159 return ans; 160 }
下面是一些练习
hdu 3727
Jewel
Initially, there is no bead at all, that is, there is an empty chain. Jimmy always sticks the new bead to the right of the chain, to make the chain longer and longer. We number the leftmost bead as Position 1, and the bead to its right as Position 2, and so on. Jimmy usually asks questions about the beads' positions, size ranks and actual sizes. Specifically speaking, there are 4 kinds of operations you should process:
Insert x
Put a bead with size x to the right of the chain (0 < x < 231, and x is different from all the sizes of beads currently in the chain)
Query_1 s t k
Query the k-th smallest bead between position s and t, inclusive. You can assume 1 <= s <= t <= L, (L is the length of the current chain), and 1 <= k <= min (100, t-s+1)
Query_2 x
Query the rank of the bead with size x, if we sort all the current beads by ascent order of sizes. The result should between 1 and L (L is the length of the current chain)
Query_3 k
Query the size of the k-th smallest bead currently (1 <= k <= L, L is the length of the current chain)

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define LL long long 6 #define clr(x) memset(x,0,sizeof(x)) 7 using namespace std; 8 struct segtree 9 { 10 int num,lc,rc; 11 }ret[4000010]; 12 struct ques 13 { 14 int tmp,l,r,k,x; 15 }que[200010]; 16 char s[100]; 17 int root[200010],a[200010],rnu,cnt,l,r,k,pos,now,n,m; 18 int BIT[200010]; 19 LL ans1,ans2,ans3; 20 void add(int i) 21 { 22 for(int x=i;x<=rnu;x+=x&-x) BIT[x]++; 23 return ; 24 } 25 int sum(int i) 26 { 27 int s; 28 for(int x=i;x>0;x-=x&-x) s+=BIT[x]; 29 return s; 30 } 31 void init(int &i,int lt,int rt); 32 void update(int &i,int num,int lt,int rt); 33 int query(int i,int j,int lt,int rt,int k); 34 int Query(int i,int j,int lt,int rt,int num); 35 int main() 36 { 37 int tt=0; 38 while(scanf("%d",&n)!=EOF) 39 { 40 printf("Case %d: ",++tt); 41 ret[0].num=ret[0].lc=ret[0].rc=0; 42 root[0]=0; 43 clr(BIT); 44 rnu=now=cnt=0; 45 ans1=ans2=ans3=0; 46 for(int i=1;i<=n;i++) 47 { 48 scanf("%s",s); 49 if(s[0]=='I') 50 { 51 que[i].tmp=0; 52 scanf("%d",&que[i].x); 53 a[++rnu]=que[i].x; 54 } 55 else 56 { 57 if(s[strlen(s)-1]=='1') 58 { 59 que[i].tmp=1; 60 scanf("%d%d%d",&que[i].l,&que[i].r,&que[i].k); 61 } 62 if(s[strlen(s)-1]=='2') 63 { 64 que[i].tmp=2; 65 scanf("%d",&que[i].x); 66 } 67 if(s[strlen(s)-1]=='3') 68 { 69 que[i].tmp=3; 70 scanf("%d",&que[i].k); 71 } 72 } 73 } 74 sort(a+1,a+rnu+1); 75 rnu=unique(a+1,a+rnu+1)-a-1; 76 init(root[0],1,rnu); 77 for(int i=1;i<=n;i++) 78 { 79 if(que[i].tmp==0) 80 { 81 now++; 82 update(root[now]=root[now-1],que[i].x,1,rnu); 83 pos=lower_bound(a+1,a+rnu+1,que[i].x)-a; 84 add(pos); 85 } 86 if(que[i].tmp==1) 87 { 88 ans1+=1LL*query(root[que[i].l-1],root[que[i].r],1,rnu,que[i].k); 89 } 90 if(que[i].tmp==2) 91 { 92 pos=lower_bound(a+1,a+rnu+1,que[i].x)-a; 93 ans2+=1LL*sum(pos); 94 } 95 if(que[i].tmp==3) 96 { 97 ans3+=1LL*query(root[0],root[now],1,rnu,que[i].k); 98 } 99 } 100 printf("%lld %lld %lld ",ans1,ans2,ans3); 101 } 102 return 0; 103 } 104 void init(int &i,int lt,int rt) 105 { 106 ret[++cnt]=ret[i]; 107 i=cnt; 108 if(lt==rt) 109 return; 110 int mid=(lt+rt)>>1; 111 init(ret[i].lc,lt,mid); 112 init(ret[i].rc,mid+1,rt); 113 return ; 114 } 115 void update(int &i,int num,int lt,int rt) 116 { 117 ret[++cnt]=ret[i]; 118 i=cnt; 119 ret[i].num++; 120 if(lt==rt) return ; 121 int mid=(lt+rt)>>1; 122 if(num<=a[mid]) 123 update(ret[i].lc,num,lt,mid); 124 else 125 update(ret[i].rc,num,mid+1,rt); 126 return ; 127 } 128 int query(int i,int j,int lt,int rt,int k) 129 { 130 int mid=(lt+rt)>>1; 131 if(lt==rt) 132 return a[rt]; 133 if(ret[ret[j].lc].num-ret[ret[i].lc].num>=k) 134 return query(ret[i].lc,ret[j].lc,lt,mid,k); 135 else 136 return query(ret[i].rc,ret[j].rc,mid+1,rt,k-(ret[ret[j].lc].num-ret[ret[i].lc].num)); 137 }

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define LL long long 6 #define clr(x) memset(x,0,sizeof(x)) 7 using namespace std; 8 struct segtree 9 { 10 int num,lc,rc; 11 }ret[4000010]; 12 struct ques 13 { 14 int tmp,l,r,k,x; 15 }que[200010]; 16 char s[100]; 17 int root[200010],a[200010],rnu,cnt,l,r,k,pos,now,n,m; 18 LL ans1,ans2,ans3; 19 int Query(int i,int j,int lt,int rt,int num); 20 void init(int &i,int lt,int rt); 21 void update(int &i,int num,int lt,int rt); 22 int query(int i,int j,int lt,int rt,int k); 23 int main() 24 { 25 int tt=0; 26 while(scanf("%d",&n)!=EOF) 27 { 28 printf("Case %d: ",++tt); 29 ret[0].num=ret[0].lc=ret[0].rc=0; 30 root[0]=0; 31 rnu=now=cnt=0; 32 ans1=ans2=ans3=0; 33 for(int i=1;i<=n;i++) 34 { 35 scanf("%s",s); 36 // printf("%s ",s); 37 if(s[0]=='I') 38 { 39 que[i].tmp=0; 40 scanf("%d",&que[i].x); 41 a[++rnu]=que[i].x; 42 // printf("%d ",que[i].x); 43 } 44 else 45 { 46 if(s[strlen(s)-1]=='1') 47 { 48 que[i].tmp=1; 49 scanf("%d%d%d",&que[i].l,&que[i].r,&que[i].k); 50 // printf("%d %d %d ",que[i].l,que[i].r,que[i].k); 51 } 52 if(s[strlen(s)-1]=='2') 53 { 54 que[i].tmp=2; 55 scanf("%d",&que[i].x); 56 // printf("%d ",que[i].x); 57 } 58 if(s[strlen(s)-1]=='3') 59 { 60 que[i].tmp=3; 61 scanf("%d",&que[i].k); 62 // printf("%d ",que[i].k); 63 } 64 } 65 } 66 // printf("-1 "); 67 sort(a+1,a+rnu+1); 68 rnu=unique(a+1,a+rnu+1)-a-1; 69 init(root[0],1,rnu); 70 for(int i=1;i<=n;i++) 71 { 72 if(que[i].tmp==0) 73 { 74 now++; 75 update(root[now]=root[now-1],que[i].x,1,rnu); 76 } 77 if(que[i].tmp==1) 78 { 79 ans1+=1LL*query(root[que[i].l-1],root[que[i].r],1,rnu,que[i].k); 80 } 81 if(que[i].tmp==2) 82 { 83 ans2+=1LL*Query(root[0],root[now],1,rnu,que[i].x); 84 } 85 if(que[i].tmp==3) 86 { 87 ans3+=1LL*query(root[0],root[now],1,rnu,que[i].k); 88 } 89 } 90 printf("%lld %lld %lld ",ans1,ans2,ans3); 91 } 92 return 0; 93 } 94 void init(int &i,int lt,int rt) 95 { 96 ret[++cnt]=ret[i]; 97 i=cnt; 98 if(lt==rt) 99 return; 100 int mid=(lt+rt)>>1; 101 init(ret[i].lc,lt,mid); 102 init(ret[i].rc,mid+1,rt); 103 return ; 104 } 105 void update(int &i,int num,int lt,int rt) 106 { 107 ret[++cnt]=ret[i]; 108 i=cnt; 109 ret[i].num++; 110 if(lt==rt) return ; 111 int mid=(lt+rt)>>1; 112 if(num<=a[mid]) 113 update(ret[i].lc,num,lt,mid); 114 else 115 update(ret[i].rc,num,mid+1,rt); 116 return ; 117 } 118 int query(int i,int j,int lt,int rt,int k) 119 { 120 int mid=(lt+rt)>>1; 121 if(lt==rt) 122 return a[rt]; 123 if(ret[ret[j].lc].num-ret[ret[i].lc].num>=k) 124 return query(ret[i].lc,ret[j].lc,lt,mid,k); 125 else 126 return query(ret[i].rc,ret[j].rc,mid+1,rt,k-(ret[ret[j].lc].num-ret[ret[i].lc].num)); 127 } 128 129 int Query(int i,int j,int lt,int rt,int num) 130 { 131 if(a[rt]<=num) 132 return ret[j].num-ret[i].num; 133 else 134 { 135 int mid=(lt+rt)>>1; 136 if(a[mid]>=num) 137 return Query(ret[i].lc,ret[j].lc,lt,mid,num); 138 else 139 return Query(ret[i].lc,ret[j].lc,lt,mid,num)+Query(ret[i].rc,ret[j].rc,mid+1,rt,num); 140 } 141 }
hdu 4605
Magic Ball Game
The rules are simple: when Kimi decides to drop a magic ball with a weight of X, the ball goes down through the tree from the root. When the magic ball arrives at a node in the tree, there's a possibility to be catched and stop rolling, or continue to roll down left or right. The game ends when the ball stops, and the final score of the game depends on the node at which it stops.
After a long-time playing, Kimi now find out the key of the game. When the magic ball arrives at node u weighting w[u], it follows the laws below:
1 If X=w[u] or node u has no children balls, the magic ball stops.
2 If X<w[u], there's a possibility of 1/2 for the magic ball to roll down either left or right.
3 If X>w[u], the magic ball will roll down to its left child in a possibility of 1/8, while the possibility of rolling down right is 7/8.
In order to choose the right magic ball and achieve the goal, Kimi wonders what's the possibility for a magic ball with a weight of X to go past node v. No matter how the magic ball rolls down, it counts if node v exists on the path that the magic ball goes along.
Manual calculating is fun, but programmers have their ways to reach the answer. Now given the tree in the game and all Kimi's queries, you're required to answer the possibility he wonders.
题意:
有一颗树,根节点为1,每一个节点要么有两个子节点,要么没有,每个节点都有一个权值wi 。然后,有一个球,附带值x 。
球到达某个节点上,如果x==wi,那么球停在这个节点上 。当然,这个点是叶子节点也会停止 。
如果x<wi,那么有1/2的概率走向左子树,有1/2的概率走向右子树 。
如果x>wi,那么有1/8的概率走向左子树,有7/8的概率走向右子树 。
问球经过v节点的概率 。(停在v节点也算)

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<string> 6 #define clr(x) memset(x,0,sizeof(x)) 7 using namespace std; 8 struct segtree 9 { 10 int l,r,val,lc,rc,num; 11 }ret[2000010]; 12 int fa[100010],w[100010],a[100010],rootl[100010],rootr[100010],n,m,l,r,k,rnu,cnt,now,q,v,x,ansl,ansr,pow2,pow7; 13 bool dir[100010]; 14 void init(int &i,int l,int r); 15 void build(int i); 16 void update(int &i,int num); 17 int query(int i,int x); 18 int main() 19 { 20 int T; 21 scanf("%d",&T); 22 ret[0].l=ret[0].r=ret[0].val=ret[0].lc=ret[0].rc=ret[0].num=0; 23 while(T--) 24 { 25 scanf("%d",&n); 26 for(int i=1;i<=n;i++) 27 { 28 scanf("%d",&w[i]); 29 fa[i]=i; 30 } 31 cnt=now=0; 32 clr(rootl); 33 clr(rootr); 34 memcpy(a,w,sizeof(w)); 35 sort(a+1,a+n+1); 36 rnu=unique(a+1,a+n+1)-a-1; 37 scanf("%d",&m); 38 for(int i=1;i<=m;i++) 39 { 40 scanf("%d%d%d",&k,&l,&r); 41 fa[l]=k; 42 fa[r]=k; 43 dir[l]=0; 44 dir[r]=1; 45 } 46 init(rootl[1],1,rnu); 47 init(rootr[1],1,rnu); 48 for(int i=2;i<=n;i++) 49 if(!rootl[i]) 50 build(i); 51 scanf("%d",&q); 52 for(int i=1;i<=q;i++) 53 { 54 scanf("%d%d",&v,&x); 55 // printf("%d %d ",query(rootl[v],x-1),query(rootr[v],x-1)); 56 if((ansl=query(rootl[v],x))+(ansr=query(rootr[v],x))!=query(rootl[v],x-1)+query(rootr[v],x-1)) 57 { 58 printf("0 "); 59 continue; 60 } 61 else 62 { 63 // printf("%d %d ",ansl,ansr); 64 pow2=ansl*3+ansr*3+ret[rootl[v]].num-ansl+ret[rootr[v]].num-ansr; 65 pow7=ansr; 66 printf("%d %d ",pow7,pow2); 67 } 68 } 69 } 70 return 0; 71 } 72 void init(int &i,int l,int r) 73 { 74 ret[++cnt]=ret[i]; 75 i=cnt; 76 ret[i].l=l; 77 ret[i].r=r; 78 ret[i].val=a[r]; 79 if(l==r) 80 return ; 81 int mid=(l+r)>>1; 82 init(ret[i].lc,l,mid); 83 init(ret[i].rc,mid+1,r); 84 return ; 85 } 86 void build(int i) 87 { 88 if(!rootl[fa[i]]) 89 build(fa[i]); 90 // printf("%d %d ",i,fa[i]); 91 if(!dir[i]) 92 { 93 rootr[i]=rootr[fa[i]]; 94 update(rootl[i]=rootl[fa[i]],w[fa[i]]); 95 } 96 else 97 { 98 rootl[i]=rootl[fa[i]]; 99 update(rootr[i]=rootr[fa[i]],w[fa[i]]); 100 } 101 return ; 102 } 103 void update(int &i,int num) 104 { 105 ret[++cnt]=ret[i]; 106 i=cnt; 107 // printf("%d %d %d %d ",ret[i].l,ret[i].r,ret[i].val,ret[i].num); 108 ret[i].num++; 109 if(ret[i].l==ret[i].r) 110 return ; 111 if(num<=ret[ret[i].lc].val) 112 update(ret[i].lc,num); 113 else 114 update(ret[i].rc,num); 115 return ; 116 } 117 int query(int i,int x) 118 { 119 if(ret[i].val<=x) 120 return ret[i].num; 121 int mid=(ret[i].l+ret[i].r)>>1; 122 if(a[mid+1]<=x) 123 return query(ret[i].lc,x)+query(ret[i].rc,x); 124 else 125 return query(ret[i].lc,x); 126 }
离线可用BIT做,想法差不多,需要将查询的结点按dfs排个序。然后瞎搞搞。这里就不再给出代码了。
SPOJ COT
COT - Count on a tree
You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight.
We will ask you to perform the following operation:
- u v k : ask for the kth minimum weight on the path from node u to node v
Input
In the first line there are two integers N and M.(N,M<=100000)
In the second line there are N integers.The ith integer denotes the weight of the ith node.
In the next N-1 lines,each line contains two integers u v,which describes an edge (u,v).
In the next M lines,each line contains three integers u v k,which means an operation asking for the kth minimum weight on the path from node u to node v.
Output
For each operation,print its result.
Example
Input:
8 5 105 2 9 3 8 5 7 7 1 2 1 3 1 4 3 5 3 6 3 7 4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2
Output: 2
8
9
105
7
建个主席树。每个结点u的线段树代表的是从根结点到该结点所有点值的权值线段树。然后用dfs离线求出所有询问的两点的LCA,以及求出每个结点的真实父亲结点。于是每个询问的答案就是u结点+v结点-LCA结点-LCA父亲结点的权值线段树上第k大了。

1 #include<bits/stdc++.h> 2 #define clr(x) memset(x,0,sizeof(x)) 3 #define clr_1(x) memset(x,-1,sizeof(x)) 4 #define LL long long 5 #define mod 1000000007 6 using namespace std; 7 const int N=2e5+10; 8 struct qnode 9 { 10 int next,to,pos,lca,ith; 11 }qedge[N*2]; 12 struct node 13 { 14 int next,to; 15 }edge[N*2]; 16 int qhead[N],head[N],qcnt,ecnt,cnt,rnu; 17 int vis[N],fa[N],value[N],ans[N],rfa[N]; 18 struct segt 19 { 20 int l,r,sum,lt,rt; 21 }seg[N<<6]; 22 int root[N],pos[N]; 23 int n,m,k,u,v; 24 void addedge(int u,int v) 25 { 26 edge[++ecnt].to=v; 27 edge[ecnt].next=head[u]; 28 head[u]=ecnt; 29 return ; 30 } 31 void addedgeq(int u,int v,int p,int val) 32 { 33 qedge[++qcnt].to=v; 34 qedge[qcnt].next=qhead[u]; 35 qedge[qcnt].ith=val; 36 qedge[qcnt].pos=p; 37 qedge[qcnt].lca=0; 38 qhead[u]=qcnt; 39 return ; 40 } 41 int Find(int x) 42 { 43 if(x!=fa[x]) 44 fa[x]=Find(fa[x]); 45 return fa[x]; 46 } 47 void Union(int u,int v) 48 { 49 if(Find(u)!=Find(v)) 50 { 51 fa[Find(v)]=Find(u); 52 } 53 return ; 54 } 55 void init(int &i,int l,int r) 56 { 57 seg[++cnt]=seg[i]; 58 i=cnt; 59 seg[i].l=l; 60 seg[i].r=r; 61 if(l==r) 62 return; 63 int mid=(l+r)>>1; 64 init(seg[i].lt,l,mid); 65 init(seg[i].rt,mid+1,r); 66 return ; 67 } 68 void update(int &i,int posi) 69 { 70 seg[++cnt]=seg[i]; 71 i=cnt; 72 seg[i].sum++; 73 if(seg[i].l==posi && seg[i].r==posi) 74 return ; 75 int mid=(seg[i].l+seg[i].r)>>1; 76 if(mid>=posi) 77 update(seg[i].lt,posi); 78 if(mid<posi) 79 update(seg[i].rt,posi); 80 return ; 81 } 82 int query(int u,int v,int lca,int lcafa,int ith) 83 { 84 if(seg[u].l==seg[u].r) 85 return seg[u].l; 86 int ul=seg[u].lt,vl=seg[v].lt,lcal=seg[lca].lt,lcafal=seg[lcafa].lt; 87 int lsum=seg[ul].sum+seg[vl].sum-seg[lcal].sum-seg[lcafal].sum; 88 if(lsum>=ith) 89 return query(ul,vl,lcal,lcafal,ith); 90 else 91 return query(seg[u].rt,seg[v].rt,seg[lca].rt,seg[lcafa].rt,ith-lsum); 92 } 93 void dfs(int u,int pre) 94 { 95 int num=lower_bound(pos+1,pos+rnu+1,value[u])-pos; 96 update(root[u]=root[pre],num); 97 rfa[u]=pre; 98 vis[u]=1; 99 for(int i=head[u];i!=-1;i=edge[i].next) 100 { 101 if(edge[i].to!=pre) 102 { 103 dfs(edge[i].to,u); 104 Union(u,edge[i].to); 105 } 106 } 107 for(int i=qhead[u];i!=-1;i=qedge[i].next) 108 { 109 if(vis[qedge[i].to]==1) 110 qedge[i].lca=Find(qedge[i].to); 111 } 112 return ; 113 } 114 void askans(int n) 115 { 116 for(int u=1;u<=n;u++) 117 { 118 for(int i=qhead[u];i!=-1;i=qedge[i].next) 119 if(qedge[i].lca!=0) 120 { 121 ans[qedge[i].pos]=pos[query(root[u],root[qedge[i].to],root[qedge[i].lca],root[rfa[qedge[i].lca]],qedge[i].ith)]; 122 } 123 } 124 return ; 125 } 126 int main() 127 { 128 while(scanf("%d%d",&n,&m)!=EOF) 129 { 130 qcnt=ecnt=cnt=0; 131 clr_1(head); 132 clr_1(qhead); 133 clr(vis); 134 for(int i=1;i<=n;i++) 135 { 136 scanf("%d",&value[i]); 137 fa[i]=i; 138 pos[i]=value[i]; 139 } 140 sort(pos+1,pos+n+1); 141 rnu=unique(pos+1,pos+n+1)-pos; 142 rnu--; 143 root[0]=0; 144 seg[0]=(segt){0,0,0,0,0}; 145 for(int i=1;i<n;i++) 146 { 147 scanf("%d%d",&u,&v); 148 addedge(u,v); 149 addedge(v,u); 150 } 151 for(int i=1;i<=m;i++) 152 { 153 scanf("%d%d%d",&u,&v,&k); 154 addedgeq(u,v,i,k); 155 addedgeq(v,u,i,k); 156 } 157 init(root[0],1,rnu); 158 dfs(1,0); 159 askans(n); 160 for(int i=1;i<=m;i++) 161 printf("%d ",ans[i]); 162 } 163 return 0; 164 }
hdu 5919
Sequence II
Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 2632 Accepted Submission(s): 702
In the i-th query, you are given two integers l
We can denote the positions(the positions according to the original sequence) where an integer appears first in this subsequence as p
Note that k
Each test case starts with two integers n (n≤2×10
There are two integers l
However, Mr. Frog thought that this problem was too young too simple so he became angry. He modified each query to l
We can denote the answers as ans
You can get the correct input l
For each test case, output one line “Case #x: p

1 #include<bits/stdc++.h> 2 #define clr(x) memset(x,0,sizeof(x)) 3 #define clr_1(x) memset(x,-1,sizeof(x)) 4 #define LL long long 5 #define mod 1000000007 6 #define INF 0x3f3f3f3f 7 using namespace std; 8 const int N=2e5+10; 9 struct node 10 { 11 int lson,rson,sum; 12 }tree[N*37]; 13 int root[N],a[N],head[N],cnt; 14 void inito(int n) 15 { 16 tree[0]=(node){0,0,0}; 17 root[n+1]=0; 18 cnt=0; 19 return ; 20 } 21 void init(int &i,int l,int r) 22 { 23 tree[++cnt]=tree[i]; 24 i=cnt; 25 tree[i]=(node){0,0,0}; 26 if(l==r) 27 return; 28 int mid=(l+r)>>1; 29 init(tree[i].lson,l,mid); 30 init(tree[i].rson,mid+1,r); 31 return ; 32 } 33 void update(int &i,int pos,int val,int lt,int rt) 34 { 35 tree[++cnt]=tree[i]; 36 i=cnt; 37 if(lt==pos && rt==pos) 38 { 39 tree[i].sum+=val; 40 return ; 41 } 42 int mid=(lt+rt)>>1; 43 if(pos<=mid) 44 update(tree[i].lson,pos,val,lt,mid); 45 else 46 update(tree[i].rson,pos,val,mid+1,rt); 47 tree[i].sum+=val; 48 return ; 49 } 50 int Find(int i,int num,int lt,int rt) 51 { 52 if(lt==rt) 53 return lt; 54 int mid=(lt+rt)>>1; 55 if(tree[tree[i].lson].sum>=num) 56 return Find(tree[i].lson,num,lt,mid); 57 else 58 return Find(tree[i].rson,num-tree[tree[i].lson].sum,mid+1,rt); 59 } 60 int query(int i,int l,int r,int lt,int rt) 61 { 62 if(lt>=l && rt<=r) 63 { 64 return tree[i].sum; 65 } 66 int ans=0; 67 int mid=(lt+rt)>>1; 68 if(mid>=l) 69 ans+=query(tree[i].lson,l,r,lt,mid); 70 if(mid<r) 71 ans+=query(tree[i].rson,l,r,mid+1,rt); 72 return ans; 73 } 74 int T,n,m,k,p,ans,li,ri,l,r; 75 int main() 76 { 77 scanf("%d",&T); 78 for(int kase=1;kase<=T;kase++) 79 { 80 scanf("%d%d",&n,&m); 81 inito(n); 82 printf("Case #%d:",kase); 83 for(int i=1;i<=n;i++) 84 { 85 scanf("%d",a+i); 86 head[a[i]]=-1; 87 } 88 init(root[n+1],1,n); 89 for(int i=n;i>=1;i--) 90 { 91 root[i]=root[i+1]; 92 if(head[a[i]]!=-1) 93 update(root[i],head[a[i]],-1,1,n); 94 head[a[i]]=i; 95 update(root[i],i,1,1,n); 96 } 97 ans=0; 98 for(int i=1;i<=m;i++) 99 { 100 scanf("%d%d",&li,&ri); 101 l=(li+ans)%n+1; 102 r=(ri+ans)%n+1; 103 if(l>r) 104 swap(l,r); 105 k=query(root[l],l,r,1,n); 106 k=(k+1)/2; 107 ans=Find(root[l],k,1,n); 108 printf(" %d",ans); 109 } 110 printf(" "); 111 } 112 return 0; 113 }