kb-09-线段树--区间合并比较繁

  1 /*
  2    hdu-1540 题意:一个线段,长度为n,三种操作,Dx,挖掉某个点;R,恢复最近被挖掉的点;Qx查询该点所在的连续区间的长度;
  3    树的节点维护三个变量,该节点左边界开始连续的个数ll,右边界开始向左连续的个数rl,(在该区间内),该区间内最大的连续区间的长度ml;
  4    最后一个变量是为了方便判断,主要的还是前两个;
  5    修改的时候先是深入单点修改单点信息,然后回溯上来更父结点,更新的时候有点复杂;
  6    查询的时候借助ml的长度减少计算量,快速返回;
  7    区间合并;
  8 */
  9 #include<cstdio>
 10 #include<iostream>
 11 #include<cstring>
 12 #include<stack>
 13 #include<algorithm>
 14 using namespace std;
 15 struct Node
 16 {
 17     int l,r,ll,rl,ml;
 18 }tr[200005];
 19 void build(int rt,int l,int r)
 20 {
 21     tr[rt].l=l;
 22     tr[rt].r=r;
 23     tr[rt].ll=tr[rt].rl=tr[rt].ml=r-l+1;
 24     if(l==r)
 25         return ;
 26     int mid=(l+r)/2;
 27     build(rt<<1,l,mid);
 28     build(rt<<1|1,mid+1,r);
 29 }
 30 void Update(int rt,int l,int r,int t,int val)//t是要改的点;
 31 {
 32     if(l==r)                                //找到单点;
 33     {
 34         if(val==1)
 35         {
 36             tr[rt].ll=tr[rt].rl=tr[rt].ml=1;//改单点的信息;
 37         }
 38         else
 39         {
 40             tr[rt].ll=tr[rt].rl=tr[rt].ml=0;
 41         }
 42         return ;
 43     }
 44     int mid=(l+r)/2;//二分查找区间;
 45     if(t<=mid)
 46     {
 47         Update(rt<<1,l,mid,t,val);
 48     }
 49     else
 50     {
 51         Update(rt<<1|1,mid+1,r,t,val);
 52     }
 53 //    pushup操作;
 54     tr[rt].ll=tr[rt<<1].ll;                                        //左子树的左侧边界的值给父节点;
 55     tr[rt].rl=tr[rt<<1|1].rl;                                    //右子树的有边界的值给父结点;
 56     tr[rt].ml=max(tr[rt<<1].ml,tr[rt<<1|1].ml);                    //左右子树的最大连续数;
 57     tr[rt].ml=max(tr[rt].ml,tr[rt<<1].rl+tr[rt<<1|1].ll);        //两个子树的交接处的最大连续数;
 58     if(tr[rt<<1].ll==mid-l+1)
 59         tr[rt].ll+=tr[rt<<1|1].ll;                                //如果左子树是全连续的,就更新父节点的左边界最大连续数;
 60     if(tr[rt<<1|1].rl==r-mid)                                    //如果右子树是连续的,就更新父结点的有边界的最大连续数;
 61         tr[rt].rl+=tr[rt<<1].rl;
 62 }
 63 int Query(int rt,int l,int r,int t)//查询与这个点连接的最长序列;
 64 {
 65     if(l==r||tr[rt].ml==0||tr[rt].ml==r-l+1)  //如果找到单点,或该区间全空,或该区间全满;就返回;
 66         return tr[rt].ml;
 67     int mid=(l+r)/2;
 68     if(t<=mid)                                    //如果该点在左子树上
 69     {
 70         if(t>=tr[rt<<1].r-tr[rt<<1].rl+1)      //如果该点是在左子树右边界点连续范围,就进入左子树继续搜索该点并加上搜索右子树的左边界那个点的结果;
 71             return Query(rt<<1,l,mid,t)+Query((rt<<1)|1,mid+1,r,mid+1);
 72         else
 73             return Query(rt<<1,l,mid,t);                     //如果不再右连续块内就进左子树继续搜索;
 74     }
 75     else//右子树;
 76     {
 77         if(t<=tr[rt<<1|1].l+tr[rt<<1|1].ll-1)       //在右子树的左侧连续的部分内,就加上左子树上的数据,左子树右边界的(不能用有子树的数据因为那不是全部的数据);
 78             return Query(rt<<1|1,mid+1,r,t)+Query(rt<<1,l,mid,mid);
 79         else
 80             return Query(rt<<1|1,mid+1,r,t);
 81     }
 82 }
 83 int main()
 84 {
 85     int n,m;
 86     while(scanf("%d%d",&n,&m)!=EOF&&n&&m)
 87     {
 88         memset(tr,0,sizeof(tr));
 89         stack<int > z;
 90         build(1,1,n);
 91         for(int i=0;i<m;i++)
 92         {
 93             char a[3];
 94             scanf("%s",a);
 95             if(a[0]==‘D‘)
 96             {
 97                 int x;
 98                 scanf("%d",&x);
 99                 z.push(x);
100                 Update(1,1,n,x,0);      //0代表删除操作;
101             }
102             else if(a[0]==‘R‘)
103             {
104                 if(!z.empty())
105                 {
106                     int x=z.top();
107                     z.pop();
108                     Update(1,1,n,x,1);   //1代表回复操作;
109                 }
110             }
111             else if(a[0]==‘Q‘)
112             {
113                 int x;
114                 scanf("%d",&x);
115                 int ans=Query(1,1,n,x);
116                 printf("%d\n",ans);
117             }
118         }
119     }
120     return 0;
121 }
时间: 2024-08-24 23:06:41

kb-09-线段树--区间合并比较繁的相关文章

poj3667 线段树 区间合并

1 //Accepted 3728 KB 1079 ms 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a document

POJ 3667 Hotel (初遇线段树区间合并)

题意: 有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作 1 w 表示在总区间内查询一个长度为w的可用区间并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出. 思路: 这是第一次遇到线段树区间合并的题目,写下感悟,还是对线段的更新和查询工作,但是查询的对象的性质已经不像单点那样,查询的是某个线段的最大可用区间是多少,还要一并

HYSBZ 1858 线段树 区间合并

1 //Accepted 14560 KB 1532 ms 2 //线段树 区间合并 3 /* 4 0 a b 把[a, b]区间内的所有数全变成0 5 1 a b 把[a, b]区间内的所有数全变成1 6 2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0 7 3 a b 询问[a, b]区间内总共有多少个1 8 4 a b 询问[a, b]区间内最多有多少个连续的1 9 */ 10 #include <cstdio> 11 #include <

HDU 3911 Black And White(线段树区间合并)

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

HDU 5316 Magician(线段树区间合并入门)

本文纯属原创,转载请注明出处谢谢.http://blog.csdn.net/zip_fan. 题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5316 Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description Fantasy magicians usually gain their ability

hdu3911 线段树 区间合并

1 //Accepted 3911 750MS 9872K 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a documen

线段树 区间合并

poj3667 Hotel 区间合并入门题,照着代码打的, 题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边       2 a b:将[a,a+b-1]的房间清空思路:记录区间中最长的空房间,开三个数组,msum[rt]表示节点rt内连续的1的个数的最大值,lsum[rt]表示从节点rt左端点开始连续1的个数,rsum[rt]表示从节点rt右端点开始连续1的个数..线段树操作:update:区间替换 query:询问满足条件的最左端点 1 #include<iostream>

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25

HDU 3308 LCIS(最长连续上升子序列)(线段树区间合并)

题意:给你n个整数,有两种操作,U A B把第A个数变成B,Q A B查询区间[A,B]的最长连续上升序列. 思路:还是查询和更新操作,而且也是询问区间中满足条件的连续最长区间 ,所以是线段树区间合并类型的题,通法是开三棵线段树,一个记录此区间内的LCIS的最长长度,一个记录从左边第一个数开始的LCIS长度,另一个记录从右边最后一个数结尾的LCIS长度.然后试图找到父亲与儿子关系维护的递推关系式就好 本题中的递推关系是: 1. 左儿子最右边的值 < 右儿子最左边的值 lmx = (左儿子的lmx