线段树的总结1
2015-09-28 20:31
483 查看
好久又没写博客了><
梅西受伤了,当神不容易啊><
这篇文章关于线段树的解题总结。
这回没有按题目难度进行讲解,主要还是因为我懒得排题目顺序><
poj3667(区间合并类问题)
题意:有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,
以2开头的是更新操作
1 w 表示在总区间内查询一个长度为w的可用区间,并且要最靠左,能找到的话返回这
个区间的左端点并占用了这个区间,找不到返回0
好像n=10 , 1 3 查到的最左的长度为3的可用区间就是[1,3],返回1,并且该区间被占
用了 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占
用),不需要输出。
在线段树里设置一个标记,用来记录是否将父亲节点的信息传递给子节点,再设置一个llen表示最大的可用长度。
再设置len与ren分别表示从左边第一个起可用的长度。查询时,首先查询左子树的len,如果符合要求就递归查询左子树,然后再查询中间横跨两个点的区间,由左子节点的ren与右节点的len组成,最后再查询右子节点。
更新的时候,如果t
.l,t
.r不是正好匹配的的话要注意将节点信息向下转移。最后不要忘记用两个子节点的信息更新父亲节点。
代码附上
2010杭州赛区I题,网上题解大多用的是并查集来解决的,但是运用线段树也是可以的。和第一题类似。稍微要转化一下。先二分答案,再将点加入线段树。
hdu 4107
一道来自阿里巴巴算法比赛的题目
时间卡的比较紧,用G++过不了貌似,用C++提交可以过。
题意:长度为N的数组,进行m次操作,每次对于给定的区间,若这个区间的数比p大.则+c,否则+2*c;
写法比较奇葩,没有用结构体,艺高人胆大吧。
还是一样的用lazy操作。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 200010
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int n,m,p;
int Max[maxn<<2];
int Min[maxn<<2];
int col[maxn<<2];
int num[maxn];
void PushUp(int rt){
Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
void Pushdown(int rt){
if(col[rt]){
col[rt<<1] += col[rt];
col[rt<<1|1] += col[rt];
Max[rt<<1] += col[rt] ;
Max[rt<<1|1] += col[rt] ;
Min[rt<<1] += col[rt] ;
Min[rt<<1|1] += col[rt] ;
col[rt] = 0;
}
}
void build(int l,int r,int rt){
Max[rt] = 0;
Min[rt] = 0;
col[rt] = 0;
if(l == r){
num[l] = 0;
return ;
}
int m = (l + r)/2;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
if(L <= l && r <= R){
if(Max[rt] < p){
col[rt] += c;
Max[rt] += c;
Min[rt] += c;
return ;
}
if(Min[rt] >= p){
col[rt] += 2 * c;
Max[rt] += 2 * c;
Min[rt] += 2 * c;
return ;
}
}
int m = (l + r)/2;
Pushdown(rt);
if(L <= m) update(L,R,c,lson);
if(m < R) update(L,R,c,rson);
PushUp(rt);
}
void query(int l,int r,int rt){
if(l == r){
num[l] += col[rt];
return ;
}
Pushdown(rt);
int m = (l + r)/2;
query(lson);
query(rson);
}
int main()
{
int a,b,c;
while(~scanf("%d %d %d",&n,&m,&p)){
build(1,n,1);
for(int i=0;i<m;i++){
scanf("%d %d %d",&a,&b,&c);
update(a,b,c,1,n,1);
}
query(1,n,1);
printf("%d",num[1]);
for(int i=2;i<=n;i++)
printf(" %d",num[i]);
printf("\n");
}
return 0;
}未完待续……
梅西受伤了,当神不容易啊><
这篇文章关于线段树的解题总结。
这回没有按题目难度进行讲解,主要还是因为我懒得排题目顺序><
poj3667(区间合并类问题)
题意:有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,
以2开头的是更新操作
1 w 表示在总区间内查询一个长度为w的可用区间,并且要最靠左,能找到的话返回这
个区间的左端点并占用了这个区间,找不到返回0
好像n=10 , 1 3 查到的最左的长度为3的可用区间就是[1,3],返回1,并且该区间被占
用了 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占
用),不需要输出。
在线段树里设置一个标记,用来记录是否将父亲节点的信息传递给子节点,再设置一个llen表示最大的可用长度。
再设置len与ren分别表示从左边第一个起可用的长度。查询时,首先查询左子树的len,如果符合要求就递归查询左子树,然后再查询中间横跨两个点的区间,由左子节点的ren与右节点的len组成,最后再查询右子节点。
更新的时候,如果t
.l,t
.r不是正好匹配的的话要注意将节点信息向下转移。最后不要忘记用两个子节点的信息更新父亲节点。
代码附上
#define rch(i) ((i)<<1|1) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define N 50010 #define INF 0x3f3f3f3f struct node { int l,r; int mark; int tlen,llen,rlen; int mid(){ return (l+r)>>1; } int cal_len(){ return r-l+1; } void updata_len(){ tlen = llen = rlen = ( mark ? 0 : cal_len() ); } }t[4*N]; void build(int l ,int r ,int rt) { t[rt].l = l; t[rt].r = r; t[rt].tlen = t[rt].llen = t[rt].rlen = t[rt].cal_len(); t[rt].mark = 0; if(l == r) return ; int mid = t[rt].mid(); build(l , mid , lch(rt)); build(mid+1 , r , rch(rt)); return ; } int query(int w ,int rt) { if(t[rt].l == t[rt].r && w == 1) //叶子特判 return t[rt].l; if(t[rt].mark != -1) //延迟标记,父亲信息传递给儿子 { t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark; t[rt].mark = -1; t[lch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况 t[rch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况 } if(t[lch(rt)].tlen >= w) //左孩子的可用区间可以满足,那么一定在左孩子区间内 return query(w , lch(rt)); else if(t[lch(rt)].rlen + t[rch(rt)].llen >= w) //横跨左右孩子且连续的区间可以满足,那么可以直接返回下标 return ( t[lch(rt)].r - t[lch(rt)].rlen + 1 ); else if(t[rch(rt)].tlen >= w) //右孩子的可用区间可以满足,那么去右孩子处找 return query(w , rch(rt)); else //找不到可用的区间 return 0; } void updata(int l ,int r ,int val ,int rt) { if(t[rt].l == l && t[rt].r == r) { t[rt].mark = val; t[rt].updata_len(); return ; } if(t[rt].mark != -1) //延迟标记,父亲信息传递给儿子 { t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark; t[rt].mark = -1; t[lch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况 t[rch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况 } int mid = t[rt].mid(); if(l > mid) //修改的区间在右孩子 updata(l , r , val , rch(rt)); else if(r <= mid) //修改的区间在左孩子 updata(l , r , val , lch(rt)); else { updata(l , mid , val , lch(rt)); updata(mid+1 , r , val , rch(rt)); } int tmp = max(t[lch(rt)].tlen , t[rch(rt)].tlen); t[rt].tlen = max(tmp , t[lch(rt)].rlen + t[rch(rt)].llen); t[rt].llen = t[lch(rt)].llen; t[rt].rlen = t[rch(rt)].rlen; if(t[lch(rt)].tlen == t[lch(rt)].cal_len() ) t[rt].llen += t[rch(rt)].llen; if(t[rch(rt)].tlen == t[rch(rt)].cal_len() ) t[rt].rlen += t[lch(rt)].rlen; return ; } int main() { int n,m; scanf("%d%d",&n,&m); build(1,n,1); while(m--) { int choose; scanf("%d",&choose); if(choose == 1) //查询操作 { int w; scanf("%d",&w); int index = query(w,1); printf("%d\n",index); if(index) updata(index , index+w-1 , 1 , 1); } else { int l,len; scanf("%d%d",&l,&len); updata(l , l+len-1 , 0 , 1); } } return 0; }
<span style="font-size:18px;">hdu3265(扫描线+线段树)</span>
#include<vector>
#include <algorithm> using namespace std; const int maxx=55555; struct node { __int64 sum,color; }tree[maxx<<3]; struct seg { int x1,x2,y,color; seg(int a,int b,int c,int d):x1(a),x2(b),y(c),color(d){} bool operator < (const seg &t)const{ return y < t.y; } }; void pushUp(int no,int l,int r) { if(tree[no].color) tree[no].sum=r-l+1; else if(l==r) tree[no].sum=0; else tree[no].sum=tree[no<<1].sum+tree[no<<1|1].sum; } void update(int x1,int x2,int color,int l,int r,int no) { if(x1<=l&&r<=x2)//整个区间涂上颜色或去掉颜色 { tree[no].color+=color; pushUp(no,l,r);//更新所涂长度 return; } int m=(l+r)>>1;//左右分别 if(x1<=m) update(x1,x2,color,l,m,no<<1);//注意等号 if(x2>m) update(x1,x2,color,m+1,r,no<<1|1); pushUp(no,l,r); } int main() { int n,x1,x2,x3,x4,y1,y2,y3,y4,i; while(cin>>n,n) { vector<seg> v; for(i=1;i<=n;++i) { cin>>x1>>y1>>x2>>y2>>x3>>y3>>x4>>y4; if(x1<x3)//拆成四个矩形 { v.push_back(seg(x1,x3,y1,1));//底边给这段涂上颜色, v.push_back(seg(x1,x3,y2,-1));//顶边把颜色去掉 } if(x4<x2) { v.push_back(seg(x4,x2,y1,1)); v.push_back(seg(x4,x2,y2,-1)); } if(y1<y3) { v.push_back(seg(x3,x4,y1,1)); v.push_back(seg(x3,x4,y3,-1)); } if(y4<y2) { v.push_back(seg(x3,x4,y4,1)); v.push_back(seg(x3,x4,y2,-1)); } } sort(v.begin(),v.end());//从小到大排序,从小的开始扫描 memset(tree,0,sizeof(tree)); __int64 ret=0; int end=v.size(); for(i=0;i<end-1;++i) { if(v[i].x2>v[i].x1) update(v[i].x1,v[i].x2-1,v[i].color,0,maxx,1);//对每条边 经行 涂颜色或去颜色 注意第二个参数-1 左闭右开,防止一个点算两次 ret+=tree[1].sum*(v[i+1].y-v[i].y);//根据所涂长度 及 高度差 算面积 } printf("%I64d\n",ret); } return 0; }需要注意一下这两道题还是有一个区别的是,第二题有重叠区域,一但某个区域被删除,但它的子节点的区域因为重叠的原因还是可以有效的。因此第一题采用了lazy操作,而第二题并没有采用此操作。
2010杭州赛区I题,网上题解大多用的是并查集来解决的,但是运用线段树也是可以的。和第一题类似。稍微要转化一下。先二分答案,再将点加入线段树。
hdu 4107
一道来自阿里巴巴算法比赛的题目
时间卡的比较紧,用G++过不了貌似,用C++提交可以过。
题意:长度为N的数组,进行m次操作,每次对于给定的区间,若这个区间的数比p大.则+c,否则+2*c;
写法比较奇葩,没有用结构体,艺高人胆大吧。
还是一样的用lazy操作。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 200010
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int n,m,p;
int Max[maxn<<2];
int Min[maxn<<2];
int col[maxn<<2];
int num[maxn];
void PushUp(int rt){
Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
void Pushdown(int rt){
if(col[rt]){
col[rt<<1] += col[rt];
col[rt<<1|1] += col[rt];
Max[rt<<1] += col[rt] ;
Max[rt<<1|1] += col[rt] ;
Min[rt<<1] += col[rt] ;
Min[rt<<1|1] += col[rt] ;
col[rt] = 0;
}
}
void build(int l,int r,int rt){
Max[rt] = 0;
Min[rt] = 0;
col[rt] = 0;
if(l == r){
num[l] = 0;
return ;
}
int m = (l + r)/2;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
if(L <= l && r <= R){
if(Max[rt] < p){
col[rt] += c;
Max[rt] += c;
Min[rt] += c;
return ;
}
if(Min[rt] >= p){
col[rt] += 2 * c;
Max[rt] += 2 * c;
Min[rt] += 2 * c;
return ;
}
}
int m = (l + r)/2;
Pushdown(rt);
if(L <= m) update(L,R,c,lson);
if(m < R) update(L,R,c,rson);
PushUp(rt);
}
void query(int l,int r,int rt){
if(l == r){
num[l] += col[rt];
return ;
}
Pushdown(rt);
int m = (l + r)/2;
query(lson);
query(rson);
}
int main()
{
int a,b,c;
while(~scanf("%d %d %d",&n,&m,&p)){
build(1,n,1);
for(int i=0;i<m;i++){
scanf("%d %d %d",&a,&b,&c);
update(a,b,c,1,n,1);
}
query(1,n,1);
printf("%d",num[1]);
for(int i=2;i<=n;i++)
printf(" %d",num[i]);
printf("\n");
}
return 0;
}未完待续……
相关文章推荐
- PHP安全编程:记住登录状态的安全做法
- 学写jquery插件
- glib学习--hash table01
- 十进制转换为二进制序列,并输出1的个数,和序列的奇偶序列
- 数据持久化的方式
- UI 第1讲 UIView
- windows android studio环境下.so文件的配置
- C/C++中结构体的区别
- Random Forests
- 使用指针实现字符串的插入
- 利用linux系统命令分析PHP程序
- 复杂对象写入文件--就是自己写的继承自NSObject类的子类
- 数学基础
- Swift - 后台获取数据(Background Fetch)的实现
- 5233杨光--Linux第二次实验
- 删除binlog的方法
- Swift - 让程序挂起后,能在后台继续运行任务
- QEMU-KVM自己主动创建虚拟机,以指定IP构造
- How to Read and Understand a Scientific Paper: A Step-by-Step Guide for Non-Scientists
- lightoj欧拉函数打表(简单)