· 单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来
o hdu1166敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和
/*************************************************************************
> File Name: hdu1166.cpp
> Author: FangPin
> Mail: fangpin1993@hotmail.com
> Created Time: 六 3/ 5 22:07:17 2016
************************************************************************/
#include<iostream>
using namespace std;
class SegmentTree{
private:
int *a;
void pushup(int rt){
a[rt]=a[rt<<1]+a[rt<<1|1];
}
public:
SegmentTree(int n){
a=new int[n<<2];
}
~SegmentTree(){
delete[] a;
}
void build(int rt,int l,int r){
if(l==r){
scanf("%d",&a[rt]);
return ;
}
else{
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
}
void update(int rt,int l,int r,int pos,int val){
if(l==r){
a[rt]+=val;
return;
}
int m=(l+r)>>1;
if(m>=pos)
update(rt<<1,l,m,pos,val);
else
update(rt<<1|1,m+1,r,pos,val);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l && y>=r)
return a[rt];
int ans=0;
int m=(l+r)>>1;
if(x<=m)
ans+=query(rt<<1,l,m,x,y);
if(y>m)
ans+=query(rt<<1|1,m+1,r,x,y);
return ans;
}
};
int main(){
int t;
scanf("%d",&t);
for(int ca=0;ca<t;++ca){
int n;
scanf("%d",&n);
SegmentTree sum(n);
sum.build(1,1,n);
printf("Case %d:\n",ca+1);
char s[10];
while(scanf("%s",s),s[0]!='E'){
if(s[0]=='Q'){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",sum.query(1,1,n,x,y));
}
else{
int pos,val;
scanf("%d%d",&pos,&val);
if(s[0]=='S')
val=-val;
sum.update(1,1,n,pos,val);
}
}
}
}
o hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值
/*************************************************************************
> File Name: hdu1754.cpp
> Author: FangPin
> Mail: fangpin1993@hotmail.com
> Created Time: 日 3/ 6 21:24:47 2016
************************************************************************/
#include<iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
class SegmentTree{
private:
int *a;
void pushup(int rt){
a[rt]=max(a[rt<<1],a[rt<<1|1]);
}
public:
SegmentTree(int n){
a=new int[n<<2];
}
~SegmentTree(){
delete[] a;
}
void build(int rt,int l,int r){
if(l==r){
scanf("%d",a+rt);
return;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int pos,int val){
if(l==r){
a[rt]=val;
return ;
}
int m=(l+r)>>1;
if(m>=pos)
update(rt<<1,l,m,pos,val);
else
update(rt<<1|1,m+1,r,pos,val);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l && y>=r)
return a[rt];
int m=(l+r)>>1;
int ans=-(1<<30);
if(x<=m)
ans=max(ans,query(rt<<1,l,m,x,y));
if(y>m)
ans=max(ans,query(rt<<1|1,m+1,r,x,y));
return ans;
}
};
int main(){
int n,m;
while(scanf("%d%d",&n,&m)==2){
SegmentTree st(n);
st.build(1,1,n);
char s[3];
int a,b;
for(int i=0;i<m;++i){
scanf("%s%d%d",s,&a,&b);
if(s[0]=='Q')
printf("%d\n",st.query(1,1,n,a,b));
else st.update(1,1,n,a,b);
}
}
return 0;
}
o hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和
/*************************************************************************
> File Name: hdu1394.cpp
> Author: FangPin
> Mail: fangpin1993@hotmail.com
> Created Time: 日 3/ 6 21:58:42 2016
************************************************************************/
#include<iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
class SegmentTree{
private:
int *a;
void pushup(int rt){
a[rt]=a[rt<<1]+a[rt<<1|1];
}
public:
SegmentTree(int n){
a=new int[n<<2];
memset(a,0,(n<<2)*sizeof(int));
}
~SegmentTree(){
delete[] a;
}
void update(int rt,int l,int r,int pos){
if(l==r){
++a[rt];
return;
}
int m=(l+r)>>1;
if(m>=pos)
update(rt<<1,l,m,pos);
else update(rt<<1|1,m+1,r,pos);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l && y>=r)
return a[rt];
int m=(l+r)>>1;
int ans=0;
if(m>=x)
ans+=query(rt<<1,l,m,x,y);
if(y>m)
ans+=query(rt<<1|1,m+1,r,x,y);
return ans;
}
};
int data[5002];
int main(){
int n;
while(~scanf("%d",&n)){
SegmentTree st(n);
int ret=0;
for(int i=1;i<=n;++i){
scanf("%d",data+i);
++data[i];
st.update(1,1,n,data[i]);
ret+=st.query(1,1,n,data[i]+1,n);
}
int ans=ret;
for(int i=1;i<n;++i){
ret+=n+1-2*data[i];
ans=min(ans,ret);
}
printf("%d\n",ans);
}
}
o hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)
/*************************************************************************
> File Name: hdu2795.cpp
> Author: FangPin
> Mail: fangpin1993@hotmail.com
> Created Time: 一 3/ 7 22:09:59 2016
************************************************************************/
#include <cstdio>
#include<iostream>
#include <algorithm>
using namespace std;
class SegmentTree{
private:
int *a;
void pushup(int rt){
a[rt]=max(a[rt<<1],a[rt<<1|1]);
}
public:
SegmentTree(int n,int w){
a=new int[n<<2];
for(int i=1;i<(n<<2);++i)
a[i]=w;
}
~SegmentTree(){
delete[] a;
}
int query(int rt,int l,int r,int w){
if(l==r){
a[rt]-=w;
return l;
}
int m=(l+r)>>1;
int ans=(a[rt<<1]>=w)?query(rt<<1,l,m,w):query(rt<<1|1,m+1,r,w);
pushup(rt);
return ans;
}
int all_max()const{
return a[1];
}
};
int main(){
int h,w,n;
while(scanf("%d%d%d",&h,&w,&n)==3){
SegmentTree st(min(h,n),w);
for(int i=0;i<n;++i){
int x;
scanf("%d",&x);
if(st.all_max()<x) puts("-1");
else printf("%d\n",st.query(1,1,min(h,n),x));
}
}
return 0;
}
poj 2828
</pre><pre code_snippet_id="1598774" snippet_file_name="blog_20160307_4_4006020" name="code" class="cpp">
/*************************************************************************
> File Name: poj2828.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 2016年03月09日 星期三 22时01分29秒
************************************************************************/
#include<iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
class SegmentTree{
private:
int *a,*b;
void pushup(int rt){
a[rt]=a[rt<<1]+a[rt<<1|1];
}
public:
SegmentTree(int n){
a=new int[n<<2];
b=new int[n];
}
~SegmentTree(){
delete[] a;
}
void build(int rt,int l,int r){
if(l==r){
a[rt]=1;
return ;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int pos,int val){
if(l==r){
a[rt]=0;
b[l]=val;
return;
}
int m=(l+r)>>1;
if(a[rt<<1]>=pos)
update(rt<<1,l,m,pos,val);
else
update(rt<<1|1,m+1,r,pos-a[rt<<1],val);
pushup(rt);
}
int getans(int pos)const{
return b[pos];
}
};
int b[200002],data[200002][2];
int main(){
int t;
while(~scanf("%d",&t)){
SegmentTree st(t);
st.build(1,1,t);
for(int i=1;i<=t;++i)
scanf("%d%d",&data[i][0],&data[i][1]);
for(int i=t;i>0;--i)
st.update(1,1,t,data[i][0]+1,data[i][1]);
for(int i=1;i<t;++i)
printf("%d ",st.getans(i));
printf("%d\n",st.getans(t));
}
return 0;
}
· 成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
o hdu1698 Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
/*************************************************************************
> File Name: hdu1698.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 2016年03月11日 星期五 22时06分04秒
************************************************************************/
#include<iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
class SegmentTree{
private:
int *a;
int *lazy;
void pushup(int rt){
a[rt]=a[rt<<1]+a[rt<<1|1];
}
void pushdown(int rt,int len){
if(lazy[rt]){
lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
a[rt<<1]=(len-(len>>1))*lazy[rt];
a[rt<<1|1]=(len>>1)*lazy[rt];
lazy[rt]=0;
}
}
public:
SegmentTree(int n){
a=new int[n<<2];
lazy=new int[n<<2];
fill(lazy,lazy+(n<<2),0);
}
~SegmentTree(){
delete []a;
delete []lazy;
}
void build(int rt,int l,int r){
if(l==r){
a[rt]=1;
return ;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int x,int y,int val){
if(x<=l && y>=r){
lazy[rt]=val;
a[rt]=(r-l+1)*val;
return;
}
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(x<=m)
update(rt<<1,l,m,x,y,val);
if(y>m)
update(rt<<1|1,m+1,r,x,y,val);
pushup(rt);
}
int ans()const{
return a[1];
}
};
int main(){
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;++ca){
int n,m;
scanf("%d%d",&n,&m);
SegmentTree st(n);
st.build(1,1,n);
for(int i=0;i<m;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
st.update(1,1,n,a,b,c);
}
printf("Case %d: The total value of the hook is %d.\n",ca,st.ans());
}
return 0;
}
o poj3468 A Simple Problem with Integers
题意:O(-1)
思路:O(-1)
线段树功能:update:成段增减 query:区间求和
/*************************************************************************
> File Name: poj3468.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 日 3/13 23:16:31 2016
************************************************************************/
#include<iostream>
#include <cstdio>
#include <cstring>
using namespace std;
class SegmentTree{
private:
long long *a,*lazy;
void pushup(int rt){
a[rt]=a[rt<<1]+a[rt<<1|1];
}
void pushdown(int rt,int len){
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
a[rt<<1]+=(len-(len>>1))*lazy[rt];
a[rt<<1|1]+=(len>>1)*lazy[rt];
lazy[rt]=0;
}
}
public:
SegmentTree(int n){
a=new long long[n<<2];
lazy=new long long[n<<2];
memset(lazy,0,(n<<2)*sizeof(long long));
}
~SegmentTree(){
delete[] a;
delete[] lazy;
}
void build(int rt,int l,int r){
if(l==r){
scanf("%lld",&a[rt]);
return ;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int x,int y,long long val){
if(x<=l && y>=r){
lazy[rt]+=val;
a[rt]+=(r-l+1)*val;
return;
}
int m=(l+r)>>1;
pushdown( rt, r-l+1);
if(x<=m)
update(rt<<1,l,m,x,y,val);
if(y>m)
update(rt<<1|1,m+1,r,x,y,val);
pushup(rt);
}
long long query(int rt,int l,int r,int x,int y){
if(x<=l && y>=r)
return a[rt];
pushdown(rt,r-l+1);
int m=(l+r)>>1;
long long ans=0;
if(x<=m)
ans+=query(rt<<1,l,m,x,y);
if(y>m)
ans+=query(rt<<1|1,m+1,r,x,y);
return ans;
}
};
int main(){
int n,m;
scanf("%d%d",&n,&m);
SegmentTree st(n);
st.build(1,1,n);
while(m--){
char s[3];
scanf("%s",s);
if(s[0]=='Q'){
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",st.query(1,1,n,a,b));
}
else{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
st.update(1,1,n,a,b,c);
}
}
return 0;
}
- 对y轴进行建树,对每条线段按横轴从小到大排序,从第1条线段开始查询并更新
- 查询该条线段所表示的区间内不同颜色数量(即可见线段数)并且记录可见线段,然后更新该区间颜色
- 最后暴力求两两可见线段数量
- 但是注意:0,4,1 和 0,2,2 和 3,4,2这三条线段覆盖的结果是区间0~4通过线段树查找可见线段是两条,其实是3条(2~3可见另一条)
- 可以把查询更新的区间*2,比如上面数据变成0,8,1 和 0,4,2 和 6,8,2则4~6之间可见一条线段
/*************************************************************************
> File Name: 1436.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 五 3/18 21:57:58 2016
************************************************************************/
#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int MAXN=8005;
struct Node{
int y1,y2,x;
}data[MAXN];
//bool hash[MAXN];
int lazy[MAXN<<3];
bool lk[MAXN][MAXN];
void build(){
memset(lazy,0,sizeof(lazy));
}
void pushdown(int rt){
if(lazy[rt]){
lazy[rt<<1]=lazy[rt];
lazy[rt<<1|1]=lazy[rt];
lazy[rt]=0;
}
}
void update(int rt,int l,int r,int x,int y,int val){
if(x<=l && y>=r){
lazy[rt]=val;
return ;
}
int m=(l+r)>>1;
pushdown(rt);
if(x<=m)
update(rt<<1,l,m,x,y,val);
if(y>m)
update(rt<<1|1,m+1,r,x,y,val);
}
void query(int rt,int l,int r,int x,int y,int pos){
if(lazy[rt]){
lk[lazy[rt]][pos]=true;
return;
}
if(l==r)
return ;
int m=(l+r)>>1;
pushdown(rt);
if(x<=m)
query(rt<<1,l,m,x,y,pos);
if(y>m)
query(rt<<1|1,m+1,r,x,y,pos);
}
void init(){
memset(lk,false,sizeof(lk));
}
bool cmp(const Node &a,const Node &b){
return a.x<b.x;
}
int main(){
int t,n;
scanf("%d",&t);
while(t--){
init();
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d%d%d",&data[i].y1,&data[i].y2,&data[i].x);
if(data[i].y1>data[i].y2)
swap(data[i].y1,data[i].y2);
data[i].y1*=2;
data[i].y2*=2;
}
sort(data,data+n,cmp);
build();
for(int i=0;i<n;++i){
query(1,1,MAXN<<1,data[i].y1,data[i].y2,i+1);
update(1,1,MAXN<<1,data[i].y1,data[i].y2,i+1);
}
int ans=0;
//. debug(n);
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(lk[i][j])
for(int k=j+1;k<=n;++k){
if(lk[i][k] && lk[j][k])
++ans;
}
}
}
printf("%d\n",ans);
}
}
· 区间合并
这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并
o poj3667 Hotel
题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空
思路:记录区间中最长的空房间
线段树操作:update:区间替换 query:询问满足条件的最左断点
/*************************************************************************
> File Name: poj3667.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 四 3/24 22:20:43 2016
************************************************************************/
#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=55555;
int sum[MAXN<<2],lsum[MAXN<<2],rsum[MAXN<<2],lazy[MAXN<<2];
void pushup(int rt,int len){
lsum[rt]=(lsum[rt<<1]==len-(len>>1))?lsum[rt<<1]+lsum[rt<<1|1]:lsum[rt<<1];
rsum[rt]=(rsum[rt<<1|1]==(len>>1))?rsum[rt<<1|1]+rsum[rt<<1]:rsum[rt<<1|1];
sum[rt]=max(max(sum[rt<<1],sum[rt<<1|1]),rsum[rt<<1]+lsum[rt<<1|1]);
}
void build(int rt,int l,int r){
sum[rt]=lsum[rt]=rsum[rt]=r-l+1;
lazy[rt]=-1;
if(l==r)
return;
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
}
void pushdown(int rt,int m){
if (lazy[rt] != -1) {
lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
sum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = lazy[rt] ? 0 : m - (m >> 1);
sum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = lazy[rt] ? 0 : (m >> 1);
lazy[rt] = -1;
}
}
int query(int rt,int l,int r,int x){
if(l==r) return l;
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(sum[rt<<1]>=x) return query(rt<<1,l,m,x);
else if(rsum[rt<<1]+lsum[rt<<1|1]>=x) return m-rsum[rt<<1]+1;
else return query(rt<<1|1,m+1,r,x);
}
void update(int rt,int l,int r,int x,int y,int val){
if(x<=l && y>=r){
lazy[rt]=val;
sum[rt]=lsum[rt]=rsum[rt]=val?0:r-l+1;
return;
}
pushdown(rt,r-l+1);
int m=(r+l)>>1;
if(x<=m)
update(rt<<1,l,m,x,y,val);
if(y>m)
update(rt<<1|1,m+1,r,x,y,val);
pushup(rt,r-l+1);
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
build(1,1,n);
for(int i=0;i<m;++i){
int op,x,y;
scanf("%d",&op);
if(op==1){
scanf("%d",&x);
if(sum[1]<x) puts("0");
else{
int ans=query(1,1,n,x);
printf("%d\n",ans);
update(1,1,n,ans,ans+x-1,1);
}
}
else{
scanf("%d%d",&x,&y);
update(1,1,n,x,x+y-1,0);
}
}
}
return 0;
}
HDU3308-LCIS-线段树区间合并
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308
题目大意:给n个数,两种操作
1:U a,b 更新第a个为b (从0开始)
2:Q a,b 查询 a,b之间LCIS(最长连续递增子序列)的长度。
其实也可以说是个模板题;三个变量保存数据ls,rs,ms分别保存从左端点开始的最长连续上升子序列,从右端点开始的最长连续上升子序列,以及这个区间的最长连续上升子序列;唯一不同的就是这里得判断一下是否能够合并;即增加一个比较如果num[mid]<num[mid+1](num数组保存的是输入的数据
/*************************************************************************
> File Name: hdu3308.cpp
> Author: Fang Pin
> Mail: fangpin1993@hotmail.com
> Created Time: 日 3/27 21:47:12 2016
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=100002;
int data[MAXN];
int a[MAXN<<2],la[MAXN<<2],ra[MAXN<<2];
void pushup(int rt,int l,int r){
int m=(l+r)>>1;
la[rt]=la[rt<<1];
if(la[rt]==m-l+1 && data[m]<data[m+1])
la[rt]+=la[rt<<1|1];
ra[rt]=ra[rt<<1|1];
if(ra[rt]==r-m && data[m]<data[m+1])
ra[rt]+=ra[rt<<1];
a[rt]=max(a[rt<<1],a[rt<<1|1]);
if(data[m]<data[m+1])
a[rt]=max(a[rt],ra[rt<<1]+la[rt<<1|1]);
}
void build(int rt,int l,int r){
if(l==r){
a[rt]=la[rt]=ra[rt]=1;
return;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt,l,r);
}
void update(int rt,int l,int r,int pos){
if(l==r){
return ;
}
int m=(l+r)>>1;
if(pos<=m)
update(rt<<1,l,m,pos);
else if(pos>m)
update(rt<<1|1,m+1,r,pos);
pushup(rt,l,r);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l && y>=r){
return a[rt];
}
int m=(l+r)>>1;
int ans=0;
if(x<=m)
ans=max(ans,query(rt<<1,l,m,x,y));
if(y>m)
ans=max(ans,query(rt<<1|1,m+1,r,x,y));
if(x<=m && y>m && data[m]<data[m+1])
ans=max(ans,min(m-x+1,ra[rt<<1])+min(y-m,la[rt<<1|1]));
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&data[i]);
}
build(1,1,n);
char s[10];
int x,y;
for(int i=0;i<m;++i){
scanf("%s%d%d",s,&x,&y);
if(s[0]=='Q'){
printf("%d\n",query(1,1,n,x+1,y+1));
}
else{
data[x+1]=y;
update(1,1,n,x+1);
}
}
}
}