线段树专题训练

来源:互联网 发布:c语言 取消包含头文件 编辑:程序博客网 时间:2024/06/03 10:26

HDU1166敌兵布阵

C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。
中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:”你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:”我知错了。。。”但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.

Input

第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令

Output

对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。

Sample Input

1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End

Sample Output

Case 1:
6
33
59

典型的单点更新,区间求和

void Update(int i,int e){//初始i=fa[n],n是跟新的军营标号    if(i==1)    {      tree[i].sum+=e;//父节点的和为左右子节点的和相加      return ;    }    tree[i].sum+=e;    Update(i>>1,e); //cout<<tree[i].sum<<' '<<i<<endl;}void Pushup(int i){    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}int Query(int left,int right,int l,int r,int i){//查询从上向下查询  left,right目标区域;  l,r当前区间大小  if(left<=l&&r<=right)//当前区域包含在,目标区域内    return tree[i].sum; //这个查询思路简单清晰,从上向下找到,包含在目标区域的和相加即可  int ans=0;  int mid=(l+r)>>1;  if(left<=mid)    ans+=Query(left,right,l,mid,i<<1);  if(right>mid)    ans+=Query(left,right,mid+1,r,i<<1|1);  return ans;}

HDU1754I Hate It

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

Input

本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M( 0 < N<=200000, 0 < M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取’Q’或’U’) ,和两个正整数A,B。
当C为’Q’的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为’U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。

Output

对于每一次询问操作,在一行里面输出最高成绩。
Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5

Sample Output

5
6
5
9

Hint

Huge input,the C function scanf() will work better than cin

单点更新区间求最值,这里的更新是覆盖式的

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#define lson l,m,i<<1#define rson m+1,r,i<<1|1using namespace std;const int maxn=200005;int tree[maxn<<2];int fa[maxn<<2];void Pushup(int i){   tree[i]=max(tree[i<<1],tree[i<<1|1]);}void Build(int l,int r,int i){    if(l==r)    {      scanf("%d",&tree[i]);      fa[l]=i;      return ;    }    int m=(l+r)>>1;    Build(lson);    Build(rson);    Pushup(i);    return;}void Update(int i){//覆盖式更新    if(i==1)    {        return ;    }    Pushup(i>>1);//更新父节点    Update(i>>1);}int Query(int L,int R,int l,int r,int i){//L,R是目标区间   if(L<=l&&R>=r)//目标区间完全包含当前区间      return tree[i];    int ans=-0x3ffff;    int m=(l+r)>>1;    if(L<=m)//左子树是目标区间的子集      ans=max(Query(L,R,lson),ans);    if(R>m)      ans=max(Query(L,R,rson),ans);    return ans;}int main(){    int N,M;    while(cin>>N>>M)    {        memset(tree,0,sizeof(tree));        Build(1,N,1);        while(M--)        {            char str;            int a,b;            cin>>str;            scanf("%d%d",&a,&b);            if(str=='Q')               printf("%d\n",Query(a,b,1,N,1));            if(str=='U')                {                    tree[fa[a]]=b;//直接覆盖式更新,将该节点的值改变Update函数只用将更新上传就好                    Update(fa[a]);                }           }    }    return 0;}

POJ 3468
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

区间更新、求和叠加式的更新

数据sum,seg范围超出int

struct node{    int left,right;    long long sum;    long long seg;}tree[maxn<<2];void Pushup(int i){    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}void Pushdown(int l,int r,int i){//操作结果延迟标记下压子区间 //同时更新子区间区间的sum    if(tree[i].seg==0)        return ;    int m=(l+r)>>1;    tree[i<<1].seg+=tree[i].seg;    tree[i<<1].sum+=tree[i].seg*(m-l+1);    tree[i<<1|1].seg+=tree[i].seg;    tree[i<<1|1].sum+=tree[i].seg*(r-m);    tree[i].seg=0;}void Build(int l,int r,int i){    tree[i].left=l;    tree[i].right=r;    if(l==r)    {        scanf("%lld",&tree[i].sum);        return ;    }    int m=(l+r)>>1;    Build(lson);    Build(rson);    Pushup(i);}void Update(int L,int R,int l,int r,int i,long long e){ //操作结果将目标区间打上延迟标记,更新路径上的sum       if(L<=l&&R>=r)       {           tree[i].seg+=e;//这里是细节           tree[i].sum+=(long long)(e*(r-l+1));           return;       }       Pushdown(l,r,i);       int m=(l+r)>>1;       if(L<=m)        Update(L,R,lson,e);       if(R>m)        Update(L,R,rson,e);       Pushup(i);}long long Query(int L,int R,int l,int r,int i){//操作结果 求区间和、跟新途中延迟标记    if(L<=l&&R>=r)    {        return tree[i].sum;    }    long long ans=0;    Pushdown(l,r,i);    int m=(l+r)>>1;    if(L<=m)        ans+=Query(L,R,lson);    if(R>m)        ans+=Query(L,R,rson);    return ans;}

HDU 1698

In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.

Now Pudge wants to do some operations on the hook.

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.

Input

The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases.
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations.
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.

Output

For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.

Sample Input

1
10
2
1 5 2
5 9 3

Sample Output

Case 1: The total value of the hook is 24.

区间更新覆盖式

void Pushup(int i){    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}void Pushdown(int l,int r,int i){    if(tree[i].seg==0)        return;    int m=(l+r)>>1;    int e=tree[i].seg;    tree[i<<1].seg=e;//本题的标记是覆盖    tree[i<<1].sum=e*(m-l+1);//注意这道题的和是覆盖    tree[i<<1|1].seg=e;    tree[i<<1|1].sum=e*(r-m);    tree[i].seg=0;}void Build(int l,int r,int i){    tree[i].left=l;    tree[i].right=r;    if(l==r)    {        tree[i].sum=1;        return ;    }    int m=(l+r)>>1;    Build(lson);    Build(rson);    Pushup(i);}void Update(int L,int R,int l,int r, int i,int e){    if(L<=l&&r<=R)    {        tree[i].seg=e;        tree[i].sum=e*(r-l+1);           return ;    }    //if(tree[i<<1].seg==e&&tree[i<<1|1].seg==e)      //  return ;    Pushdown(l,r,i);    int m=(l+r)>>1;    if(L<=m)    Update(L,R,lson,e);    if(R>m)    Update(L,R,rson,e);    Pushup(i);}int Query(int L,int R,int l, int r,int i){    if(L<=l&&r<=R)        return tree[i].sum;    Pushdown(l,r,i);    int m=(l+r)>>1;    int ans=0;    if(L<=m)        ans+=Query(L,R,lson);    if(R>m)        ans+=Query(L,R,rson);    return ans;}

ZOJ 1610
Painting some colored segments on a line, some previously painted segments may be covered by some the subsequent ones.
Your task is counting the segments of different colors you can see at last.

Input

The first line of each data set contains exactly one integer n, 1 <= n <= 8000, equal to the number of colored segments.
Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces:

x1 x2 c

x1 and x2 indicate the left endpoint and right endpoint of the segment, c indicates the color of the segment.

All the numbers are in the range [0, 8000], and they are all integers.

Input may contain several data set, process to the end of file.

Output

Each line of the output should contain a color index that can be seen from the top, following the count of the segments of this color, they should be printed according to the color index.
If some color can’t be seen, you shouldn’t print it.

Print a blank line after every dataset.

Sample Input

5
0 4 4
0 3 1
3 4 2
0 2 2
0 2 3
4
0 1 1
3 4 1
1 3 2
1 3 1
6
0 1 0
1 2 1
2 3 1
1 2 0
2 3 0
1 2 1

Sample Output

1 1
2 1
3 1
1 1

0 2
1 1

线段更新、涂色问题,化线段为点,更新时Update(a+1,b,1,n,1,e);详情看代码

/*区间涂色问题,没有Pushup*//*输出能够看见的颜色的序号和长度*//*因为这道例题的数据范围是[0,8000]所以选择左区间加一将区间点化更新[left,right]==Update[left+1,right])有区间值代表[right-1,right]这个区间*/#include<iostream>#include<cstdio>#include<cstring>#define lson l,m,i<<1#define rson m+1,r,i<<1|1using namespace std;const int maxn=8005;int col[maxn<<2],seg[maxn<<2];int fa[maxn<<2];//记录叶子结点的下标int ans[maxn];//记录不同颜色的数量void Pushdown(int l,int r,int i){    if(seg[i]!=-1)    {      seg[i<<1]=seg[i];      col[i<<1]=seg[i];      seg[i<<1|1]=seg[i];      col[i<<1|1]=seg[i];      seg[i]=-1;    }}void Build(int l,int r,int i){    if(l==r)    {        fa[l]=i;        return;    }    int m=(l+r)>>1;    Build(lson);    Build(rson);    return;}void Update(int L,int R,int l,int r,int i,int c){ //更新时L=left,R=right+1;(这样可以用点代替区间)       if(L<=l&&r<=R)       {           seg[i]=c;           col[i]=c;           return;       }       Pushdown(l,r,i);       int m=(l+r)>>1;       if(L<=m)        Update(L,R,lson,c);       if(R>m)        Update(L,R,rson,c);       return;}void Query(int l,int r,int i){//操作结果将叶子结点全部更新    if(l==r)    {        col[i]=seg[i];        return ;    }    Pushdown(l,r,i);    int m=(l+r)>>1;    Query(lson);    Query(rson);    return;}void Count(){//计算叶子结点的不同颜色种类     for(int i=0;i<8000;i++)     {         int t=fa[i],p=fa[i+1];         if(col[t]!=-1)           {               if(col[t]!=col[p])                ans[col[t]]++;           }     }     if(col[8000]!=-1&&col[fa[8000]]!=col[fa[7999]])          ans[col[fa[8000]]]++;}int main(){    int n;    while(cin>>n)    {        memset(col,-1,sizeof(col));        memset(seg,-1,sizeof(seg));        memset(fa,0,sizeof(fa));        memset(ans,0,sizeof(ans));        Build(0,8000,1);        while(n--)        {            int a,b,c;            scanf("%d%d%d",&a,&b,&c);            if(a<b)//注意这点a==b 更新的不是片段                Update(a+1,b,0,8000,1,c);        }        Query(0,8000,1);//更新叶子结点        Count();       for(int i=0;i<=8000;i++)       {           if(ans[i]!=0)            printf("%d %d\n",i,ans[i]);       }       printf("\n");    }    return 0;}

POJ2528
The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules:
Every candidate can place exactly one poster on the wall.
All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown).
The wall is divided into segments and the width of each segment is one byte.
Each poster must completely cover a contiguous number of wall segments.

They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections.
Your task is to find the number of visible posters when all the posters are placed given the information about posters’ size, their place and order of placement on the electoral wall.

Input

The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers l i and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= l i <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered l i, l i+1 ,… , ri.

Output

For each input data set print the number of visible posters after all the posters are placed.

The picture below illustrates the case of the sample input.

Sample Input

1
5
1 4
2 6
8 10
3 4
7 10

Sample Output

4

线段树+离散化

在1 <= i <= n, 1 <= l i <= ri <= 10000000的范围上张贴海报,海报的数目1 <= n <= 10000 也就是说最多涉及到的坐标只有20000个这个数据远小于10000000,按照普通情况下建造的线段树肯定超时因此就涉及到了离散化处理坐标。
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
1-10 1-4 5-10
1-10 1-4 6-10
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.

//现在副本数组中存储原先坐标,排序,去重,填补空隙//原坐标的映射就是ls数组中对应的位置void dis()//坐标离散化{    int c=0;   for(int i=1;i<=n;i++)   {       scanf("%d%d",&Left[i],&Right[i]);       ls[c++]=Left[i];       ls[c++]=Right[i];   }   sort(ls,ls+c);   int m=unique(ls,ls+c)-ls;   int t=m;   for(int i=1;i<t;i++)   {       if(ls[i]>ls[i-1]+1)        ls[m++]=ls[i-1]+1;//填补空隙   }   sort(ls,ls+m);   for(int i=1;i<=n;i++)       {           int x=lower_bound(ls,ls+m,Left[i])-ls;           int y=lower_bound(ls,ls+m,Right[i])-ls;           Update(x,y,0,m-1,1,i);       }       Query(0,m-1,1);}

HDU 4027
A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.

Notice that the square root operation should be rounded down to integer.

Input

The input contains several test cases, terminated by EOF.
For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)
The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. You can assume that the sum of all endurance value is less than 2^63.
The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)
For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.

Output

For each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.

Sample Input

10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8

Sample Output

Case #1:
19
7
6

区间更新+剪枝

一排带有生命值的战舰,用秘密武器攻击每次使其生命值变为原来的平方根,最后区间求和You can assume that the sum of all endurance value is less than 2^63. 数据范围最大2^63最多开7次根号,或者说当区间内的值都等一1的时候不用再更新
注意一点更新查询时的左右坐标left可能大于right
不帶延迟标记的代码

struct node{    ll sum;//记录的当前生命值}tree[maxn<<2];void Pushup(int i){   tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}void Build(int l,int r,int i){    if(l==r)    {        scanf("%lld",&tree[i].sum);        return ;    }    int m=(l+r)>>1;    Build(lson);    Build(rson);    Pushup(i);}//由于数据最大是2^63  最多开7次根号因此在update是添加一个判断即可void Update(int L,int R,int l,int r,int i){    if(tree[i].sum==(r-l+1))//如果区间里的值都是1 就不用在开根号了        return;    if(l==r)    {        tree[i].sum=(ll)sqrt(tree[i].sum);        return ;    }    int m=(l+r)>>1;    if(L<=m)        Update(L,R,lson);    if(R>m)        Update(L,R,rson);        Pushup(i);}ll Query(int L,int R,int l,int r,int i){       if(L<=l&&r<=R)       {           return tree[i].sum;       }       ll ans=0;      int m=(l+r)>>1;      if(L<=m)            ans+=Query(L,R,lson);        if(R>m)            ans+=Query(L,R,rson);    return ans;}

HDU1540
During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.

Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!

Input

The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

D x: The x-th village was destroyed.

Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.

Output

Output the answer to each of the Army commanders’ request in order on a separate line.

Sample Input

7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4

Sample Output

1
0
2
4

新技能线段树区间维护、查找含有某个点的最大连续区间长度

一排村庄初始化是连续的,D x摧毁村庄x,R修复最近被摧毁的村庄,Q x查询x包含在内的最大连续长度
线段树维护区间的 最大连续长度、最大左连续长度、最大右连续长度
len 区间最大连续长度、llen区间最大左连续长度(从左向右找的最长连续长度)、rlen区间最大右连续长度(从右向左找的最长连续长度)

void Pushup(int l,int r,int i){    int m=(l+r)>>1;     len[i]=max(len[i<<1],len[i<<1|1]);     len[i]=max(len[i],rlen[i<<1]+llen[i<<1|1]);     llen[i]=llen[i<<1];     rlen[i]=rlen[i<<1|1];     if(len[i<<1]==m-l+1)//左子树完全连续  当前区间的左连续长度等于左子树的连续长度+右子树的左连续长度        llen[i]+=llen[i<<1|1];     if(len[i<<1|1]==r-m)//右子树完全连续  同理        rlen[i]+=rlen[i<<1];}void Update(int l,int r,int i,int p,int e){    if(l==r&&l==p)    {        len[i]=rlen[i]=llen[i]=e;        return ;    }    int m=(l+r)>>1;    if(p<=m)       Update(lson,p,e);    else       Update(rson,p,e);    Pushup(l,r,i);}

查询的时候 如果包含目标点p的区间的长度len==0或者len==r-l+1 即该区间完全不连续、或者完全连续。直接返回区间连续长度即可
如果p在区间左子树的右连续区间或者右子树的左连续区间 返回rlen[i<<1]+llen[i<<1|1]
否则继续向子树查找,总数出现上述三种情况

int Query(int l,int r,int i,int p){    if(len[i]==0||len[i]==(r-l+1))//包含p点的区间完全连续,或者完全不连续        return len[i];    int m=(l+r)>>1;    if(p<=m) //这三行代码很美  很简洁        return p>m-rlen[i<<1]?rlen[i<<1]+llen[i<<1|1]:Query(lson,p);//p<=m   p>m-rlen[i]  说明p在左子树的右连续区间    return p<=m+llen[i<<1|1]?rlen[i<<1]+llen[i<<1|1]:Query(rson,p);//p>m   p<m+llen[i<<1|1] 说明p在右子树的左连续区间}
#include<iostream>#include<stack>#include<cstring>#include<algorithm>#include<cstdio>#define lson l,m,i<<1#define rson m+1,r,i<<1|1using namespace std;const int maxn=50005;int len[maxn<<2],llen[maxn<<2],rlen[maxn<<2];int main(){    char op[10];    int p;    int n,m;    while(~scanf("%d%d",&n,&m))    {        stack<int>s;//存储最近被摧毁的村庄        Build(1,n,1);        while(m--)        {            scanf("%s",op);            if(op[0]=='R'&&!s.empty())            {                p=s.top();                s.pop();                Update(1,n,1,p,1);//修复            }            if(op[0]=='D')            {                scanf("%d",&p);                Update(1,n,1,p,0);                s.push(p);            }           if(op[0]=='Q')           {               scanf("%d",&p);               printf("%d\n",Query(1,n,1,p));           }        }    }    return 0;}
原创粉丝点击