【完全版】线段树

先膜拜大牛 orz orz orz .... .. .. . . .. .  http://www.notonlysuccess.com/index.php/segment-tree-complete/

maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
PushUP(int rt)是把当前结点的信息更新到父结点
PushDown(int rt)是把当前结点的信息更新给儿子结点
rt表示当前子树的根(root),也就是当前所在的结点
整理这些题目后我觉得线段树的题目整体上可以分成以下四个部分:


单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来


hdu1166 敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和

#include<iostream>
#include<cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=50005;
int sum[maxn<<2];
void PushUP(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r){
        scanf("%d",&sum[rt]);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
            return sum[rt];
    }
    int m=(l+r)>>1;
    int ret=0;
    if(L<=m)ret+=query(L,R,lson);
    if(R>m)ret+=query(L,R,rson);
    return ret;
}
void update(int p,int add,int l,int r,int rt){
    if(l==r){
        sum[rt]+=add;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m)update(p,add,lson);
    else update(p,add,rson);
    PushUP(rt);
}
int main()
{
    int T,n,k=1;
    scanf("%d",&T);
    while(T--)
    {
        printf("Case %d:\n",k++);
        scanf("%d",&n);
        build(1,n,1);
        char op[10];
        while(scanf("%s",op))
        {
            if(op[0]=='E')break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(op[0]=='Q')printf("%d\n",query(a,b,1,n,1));
            else if(op[0]=='S')update(a,-b,1,n,1);
            else update(a,b,1,n,1);
        }
    }
    return 0;
}

hdu1754 I Hate It
题意:O(-1)
思路:O(-1)

线段树功能:update:单点替换 query:区间最值

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int MAX[maxn<<2];
void PushUP(int rt)
{
    MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt)
{
    if(l==r){
        scanf("%d",&MAX[rt]);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
            return MAX[rt];
    }
    int m=(l+r)>>1;
    int ret=0;
    if(L<=m)ret=max(ret,query(L,R,lson));
    if(R>m)ret=max(ret,query(L,R,rson));
    return ret;
}
void update(int p,int val,int l,int r,int rt){
    if(l==r){
        MAX[rt]=val;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m)update(p,val,lson);
    else update(p,val,rson);
    PushUP(rt);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        while(m--)
        {
            char op[2];
            int a,b;
            scanf("%s%d%d",op,&a,&b);
            if(op[0]=='Q')printf("%d\n",query(a,b,1,n,1));
            else update(a,b,1,n,1);
        }
    }
    return 0;
}


hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和


#include <cstdio>
#include <algorithm>
using namespace std;

#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 5555;
int sum[maxn<<2];
void PushUP(int rt) {
	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt) {
	sum[rt] = 0;
	if (l == r) return ;
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
}
void update(int p,int l,int r,int rt) {
	if (l == r) {
		sum[rt] ++;
		return ;
	}
	int m = (l + r) >> 1;
	if (p <= m) update(p , lson);
	else update(p , rson);
	PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
	if (L <= l && r <= R) {
		return sum[rt];
	}
	int m = (l + r) >> 1;
	int ret = 0;
	if (L <= m) ret += query(L , R , lson);
	if (R > m) ret += query(L , R , rson);
	return ret;
}

struct ss
{
    int num,id;
}a[maxn];
bool cmp(ss a,ss b)
{
    return a.num<b.num;
}
int x[maxn];
int main() {
	int n;
	while (~scanf("%d",&n)) {
		build(0 , n - 1 , 1);
		int sum = 0;
		///离散化
		for (int i = 0 ; i < n ; i ++) {
                scanf("%d",&a[i].num);
                a[i].id=i;
		}
		sort(a,a+n,cmp);
        x[a[0].id]=0;
        for(int i=1;i<n;i++)
        {
            if(a[i].num!=a[i-1].num)
                   x[a[i].id]=i;
            else
                   x[a[i].id]=x[a[i-1].id];
        }
		///for (int i = 0 ; i < n ; i ++) printf("%d %d\n",i,x[i]);

		for (int i = 0 ; i < n ; i ++) {
			sum += query(x[i]+1 , n - 1 , 0 , n - 1 , 1);
			//The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
			update(x[i] , 0 , n - 1 , 1);
		}
		int ret = sum;
		for (int i = 0 ; i < n ; i ++) {
			sum += n - x[i] - x[i] - 1;
			ret = min(ret , sum);
		}
		printf("%d\n",ret);
	}
	return 0;
}


hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int MAX[maxn<<2];
int n,h,w,x,k;
void PushUP(int rt)
{
    MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt)
{
    MAX[rt]=w;//比放在if里面要时间,放if里面的话最后一行要加PushUP(rt);
    if(l==r)return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
int query(int l,int r,int rt)
{
    if(l==r){
        MAX[rt]-=x;
        return l;
    }
    int m=(l+r)>>1;
    int ret=MAX[rt<<1]>=x?query(lson):query(rson);
    //这样只会改变一个节点,真机智啊
    PushUP(rt);
    return ret;
}
int main()
{
    while(~scanf("%d%d%d",&h,&w,&n))
    {
        k=min(h,n);
        build(1,k,1);
        for(int i=0;i<n;i++){
            scanf("%d",&x);
            if(x>MAX[1])printf("-1\n");
            else {
                printf("%d\n",query(1,k,1));
            }
        }
    }
    return 0;
}


POJ2828

题意:有n个人一次到达售票队列,可以插队,问最后队列状态
一开始想着按照插队位置和到达先后顺序sort一下,发现第二组案例都过不了
sort会破坏队伍原型,如下三个案例答案是不同的,sort后就无差别了

4
0 20523
0 31492
1 19243
1 3890
4
0 20523
1 19243
1 3890
0 31492
4
0 20523
1 19243
0 31492
1 3890
此题可以用线段树做,节点表示剩余空位数,由于,题目保证后到的人m前面一定有m-1人(Posi ∈ [0, i − 1] ),所以按输入顺序倒着单点更新即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int Sum[maxn<<2];
struct ss{
    int pos,val,id;
}a[maxn];
int ans[maxn];
int id;
bool cmp(ss a,ss b)
{
    return a.id<b.id;
}
void PushUP(int rt)
{
    Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    Sum[rt]=r-l+1;//小优化节省50ms
    if(l==r){
     //   Sum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
   // PushUP(rt);
}
void query(int p,int l,int r,int rt)
{
   // printf("%d l=%d r=%d rt=%d %d\n",p,l,r,rt,Sum[rt]);
    Sum[rt]--;//小优化节省50ms
    if(l==r){
        Sum[rt]=0;
        id=l;
        return;
    }
    int m=(l+r)>>1;
    if(Sum[rt<<1]>=p)query(p,lson);
    else query(p-Sum[rt<<1],rson);
  //  PushUP(rt);
}
int main()
{
    int n,i;
    while(~scanf("%d",&n)){
        build(1,n,1);
        for(i=0;i<n;i++){
            scanf("%d%d",&a[i].pos,&a[i].val);
        }
        for(i=n-1;i>=0;i--){
            query(a[i].pos+1,1,n,1);
            ans[id]=a[i].val;
        }
       // sort(a,a+n,cmp);//大优化可节省500ms多
        for(i=1;i<=n;i++){
            if(i==n)printf("%d\n",ans[i]);
            else printf("%d ",ans[i]);
        }
    }
    return 0;
}

POJ2886

题意:n个熊孩子按顺时针围成一个圈,每个孩子手中有张卡片,卡片上有数字x (x!=0) ,第一轮从第k个孩子起出列,若x<0,则从他的左边数第x个出列,若x>0,则从他的右边数(-x)个出列,直到只剩一个孩子,每个出列的孩子都会获得f(p)颗糖果,问f(p)最值。f(p)表示p的因子数,p表示出列的次序。

思路:线段数 先算出N个人中,是第几个人(id)跳出来得到的糖果最多。然后模拟id遍 长到第id个人的name
线段树的结点中保存该区间内还剩多少人,每次update 删除一个人。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=500005;
int sum[maxn<<2];
int pos;
struct ss
{
    char name[12];
    int val;
}a[maxn];
int yin[maxn];
int getyin(int n)
{
    int i,j;
    yin[1]=0;
    for(i=1;i<=n;i++)
    {
        for(j=i;j<=n;j+=i)yin[j]++;
    }
   //如果把因子打表,单独算n=500005比每次算慢500ms,不知道为什么

   // return max_element(yin+1,yin+n+1)-yin; 时间慢50ms
    int ma=yin[1],id=i;
    for(i=2;i<=n;i++){
        if(yin[i]>ma){
            ma=yin[i];
            id=i;
        }
    }
    return id;
}
void build(int l,int r,int rt)
{
    sum[rt]=r-l+1;
    if(l==r){
        int x;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void query(int p,int l,int r,int rt){
    sum[rt]--;
    if(l==r){
        pos=l;
        return;
    }
    int m=(l+r)>>1;
    if(sum[rt<<1]>=p)query(p,lson);
    else query(p-sum[rt<<1],rson);
}
int main()
{
    int n,k,i;
    while(~scanf("%d%d",&n,&k))
    {
        build(1,n,1);
        for(i=1;i<=n;i++)scanf("%s%d",a[i].name,&a[i].val);
        int m=getyin(n)-1;
        int mod=n-1;
        query(k,1,n,1);
       // printf("pos= %d,m= %d\n",pos,yin[m+1]);
	//原先是从1-n都算一遍,慢500ms
        for(i=0;i<m;i++)
        {
            //从左起第k个出列
            if(a[pos].val>0)k=(k-1+a[pos].val-1)%mod+1;
            else k=((k-1+a[pos].val)%mod+mod)%mod+1;
            query(k,1,n,1);
           // printf("pos=%d %d k=%d\n",pos,mod, k);
            mod--;
        }
        printf("%s %d\n",a[pos].name,yin[m+1]);
    }
    return 0;
}




成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
hdu1698 Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=100005;
int sum[maxn<<2],col[maxn<<2];
void PushUP(int rt)
{
   // printf("pushup :rt= %d \n",rt);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
    if(col[rt]){
    //    printf("pushdown :rt= %d  m=%d\n",rt,m);
        col[rt<<1]=col[rt<<1|1]=col[rt];
        sum[rt<<1]=(m-(m>>1))*col[rt];
        sum[rt<<1|1]=(m>>1)*col[rt];
        col[rt]=0;
    }
}
void build(int l,int r,int rt)
{
    col[rt]=0;//延迟更新标记
    if(l==r){
        sum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
            return sum[rt];
    }
    int m=(l+r)>>1;
    int ret=0;
    if(L<=m)ret+=query(L,R,lson);
    if(R>m)ret+=query(L,R,rson);
    return ret;
}
void update(int L,int R,int c,int l,int r,int rt){
  //  printf("----- %d %d %d\n",l,r,rt);
    if(l>=L&&r<=R){
        col[rt]=c;
        sum[rt]=c*(r-l+1);
        return;
    }
    PushDown(rt,r-l+1);
    int m=(l+r)>>1;
    if(L<=m)update(L,R,c,lson);
    if(R>m)update(L,R,c,rson);
    PushUP(rt);
}
int main()
{
    int n,m,T,k=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            update(a,b,c,1,n,1);
        }
        printf("Case %d: The total value of the hook is %d.\n",k++,query(1,n,1,n,1));
    }
    return 0;
}


POJ3468
题意:O(-1)
思路:O(-1)
线段树功能:update:成段增减 query:区间求和

用时1719ms 用数组数组会更快http://kenby.iteye.com/blog/962159

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
const int maxn=100005;
LL sum[maxn<<2],add[maxn<<2];
void PushUP(int rt)
{
   // printf("pushup :rt= %d \n",rt);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
    if(add[rt]){
    //    printf("pushdown :rt= %d  m=%d\n",rt,m);
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(m-(m>>1))*add[rt];
        sum[rt<<1|1]+=(m>>1)*add[rt];
        add[rt]=0;
    }
}
void build(int l,int r,int rt)
{
    add[rt]=0;//延迟更新标记
    if(l==r){
        scanf("%lld",&sum[rt]);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUP(rt);
}
LL query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
            return sum[rt];
    }
    PushDown(rt,r-l+1);//访问子节点1前都要pushdown
    int m=(l+r)>>1;
    LL ret=0;
    if(L<=m)ret+=query(L,R,lson);
    if(R>m)ret+=query(L,R,rson);
    return ret;
}
void update(int L,int R,int c,int l,int r,int rt){
  //  printf("----- %d %d %d\n",l,r,rt);
    if(l>=L&&r<=R){
        add[rt]+=(LL)c;
        sum[rt]+=(LL)c*(r-l+1);
        return;
    }
    PushDown(rt,r-l+1);//访问子节点2前都要pushdown
    int m=(l+r)>>1;
    if(L<=m)update(L,R,c,lson);
    if(R>m)update(L,R,c,rson);
    PushUP(rt);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        while(m--)
        {
            char op[2];
            int a,b,c;
            scanf("%s",op);
            if(op[0]=='C'){
                scanf("%d%d%d",&a,&b,&c);
                update(a,b,c,1,n,1);
            }
            else{
                scanf("%d%d",&a,&b);
                printf("%I64d\n",query(a,b,1,n,1));
            }
        }
    }
    return 0;
}



POJ2528

题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[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,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖

为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=10010;
int ll[maxn],rr[maxn];
int X[maxn<<2];
int col[maxn<<4];
bool vis[maxn<<2];
int cnt;
void PushDown(int rt){
    if(col[rt]!=-1){
        col[rt<<1]=col[rt<<1|1]=col[rt];
        col[rt]=-1;//懒惰标记向下转移,上层标记一定要去除,否则更新的还是上层的值
    }
}
void build(int l,int r,int rt)
{
    col[rt]=-1;
    if(l==r){
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void update(int L,int R,int c,int l,int r,int rt){
    if(l>=L&&r<=R){
        col[rt]=c;
        return;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(L<=m)update(L,R,c,lson);
    if(R>m)update(L,R,c,rson);
}
void query(int l,int r,int rt){
    if(l==r){
        if(col[rt]!=-1&&!vis[col[rt]]){//col[rt]!=-1 有的点没有被更新
            vis[col[rt]]=1;
            cnt++;
        }
        return;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    query(lson);
    query(rson);
}
int Bin(int key,int n,int X[]) {
	int l=0,r=n-1,mid;
	while (l<r) {
		mid=(l+r)>>1;
		if(X[mid]>=key)r=mid;
		else l=mid+1;
	}
	return r;
}
int main()
{
    int T,i;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        int nn=0;
        for(i=0;i<n;i++){
            scanf("%d%d",&ll[i],&rr[i]);
            X[nn++]=ll[i];
            X[nn++]=rr[i];
        }
        sort(X,X+nn);
        int m=1;
        for(i=1;i<nn;i++)if(X[i]!=X[i-1])X[m++]=X[i];

        for(i=m-1;i>0;i--){
            if(X[i]!=X[i-1]+1)X[m++]=X[i-1]+1;
        }
        sort(X,X+m);
        build(1,m,1);//用build与memset时间上没有差别,由于不用求和等操作,用memset更简单
       // memset(col,-1,sizeof(col));
        for(i=0;i<n;i++){
            int l=Bin(ll[i],m,X)+1;//这里用map去查找超时,用二分就不超,不知道为什么
	        int r=Bin(rr[i],m,X)+1;
            update(l,r,i,1,m,1);
        }
        memset(vis,0,sizeof(vis));
        cnt=0;
        query(1,m,1);
        printf("%d\n",cnt);
    }
    return 0;
}
/*
2
3
1 10
1 4
5 10
3
1 10
1 4
6 10
*/


poj3225 Help with Intervals
题意:区间操作,交,并,补等
思路:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换
成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记


开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)

线段树功能:update:成段替换,区间异或 query:简单hash

10次WA,总算过了,先贴了,还有的地方不明白

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=65536*2;
int col[maxn<<2],yihuo[maxn<<2];
int vis[maxn];
void Fyihuo(int rt)
{
    if(col[rt]!=-1)col[rt]^=1;
    else yihuo[rt]^=1;
}
void PushDown(int rt)
{
    if(col[rt]!=-1)
    {
        col[rt<<1]=col[rt<<1|1]=col[rt];
        yihuo[rt<<1]=yihuo[rt<<1|1]=yihuo[rt]=0;
        col[rt]=-1;
    }
    if(yihuo[rt]){
        Fyihuo(rt<<1);
        Fyihuo(rt<<1|1);
        yihuo[rt]=0;
    }
}
void update(char op,int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R){
        if(op=='U'){
            col[rt]=1;
            yihuo[rt]=0;
        }
        else if(op=='D'){
            col[rt]=0;
            yihuo[rt]=0;
        }
        else if(op=='C'||op=='S'){
            Fyihuo(rt);
        }
        return ;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(m>=L)update(op,L,R,lson);
    else if(op=='I'||op=='C'){
        col[rt<<1]=yihuo[rt<<1]=0;
    }
    if(m<R)update(op,L,R,rson);
    else if(op=='I'||op=='C'){
        col[rt<<1|1]=yihuo[rt<<1|1]=0;
    }
    //学习了,(L,R)以外区间的更新方法

}
void query(int l,int r,int rt)
{
    if (col[rt] == 1) {
		for (int it = l ; it <= r ; it ++) {
			vis[it] = true;
		}
		return ;
	} else if (col[rt] == 0) return ;
	/*
	if(l==r){
        if(col[rt]==1)vis[l]=1;
        else vis[l]=0;
        //if(l<12)printf("%d %d\n",l,col[rt]);
        return;
    }
    *///不知为什么WA
    PushDown(rt);
    int m=(l+r)>>1;
    query(lson);
    query(rson);
}
int main()
{
   // freopen("test.in","r",stdin);
	char op,x,y;
    int a,b;
    memset(col,0,sizeof(col));//初始化为-1就WA
    memset(yihuo,0,sizeof(yihuo));
    while(~scanf("%c %c%d,%d%c\n",&op,&x,&a,&b,&y))
    {
       // getchar();
        a <<= 1 ;
        b <<= 1;
		if (x == '(') a ++;
		if (y == ')') b --;
		update(op , a , b , 0 , maxn , 1);
    }
	query(0 , maxn , 1);
	int f=1,flag=0;
	for(int i=0;i<maxn;i++)
    {
      //  printf("%d %d\n",i,vis[i]);
        if(vis[i]&&f==1){
            if(flag)printf(" ");
            if(i%2==0)printf("[%d,",i/2);
            else printf("(%d,",i/2);
            flag=1;
            f=0;
        }
        if(!vis[i]&&f==0){
            if((i-1)%2==0)printf("%d]",i/2);
            else printf("%d)",i/2);
            f=1;
        }
    }
	if (!flag) printf("empty set");
	puts("");

    return 0;
}

Poj 3667 - Hotel 线段树--区间合并

最近一直在看胡浩的【完全版】线段树,这个题目是在他的blog介绍的

 

文字没有参考别人的成分

 

题目大意:Hotel有N(1 ≤ N ≤ 50,000)间rooms,并且所有的rooms都是连续排列在同一边,groups需要check in 房间,要求房间的编号为连续的r..r+Di-1并且r是最小的;visitors同样可能check out,并且他们每次check out都是编号为Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1)的房间,题目的输入有两种样式:

  1. 1  a     :  groups需要check in  a间编号连续的房间
  2. 2  a   b : visitors  check out 房间,其中房间编号是 a…a+b-1

要求对于每次request,输出为groups分配数目为a的房间中编号最小的房间编号

 

思路:利用线段树建立模型,维护最大连续区间长度,其中区间长度就是对应的房间数目,并且对应区间中最左边的断点就是answer,同时因为需要求出连续区间的最大长度,因此每次PushUp时都将左右区间合并,lsum维护左区间的最大长度,rsum维护右区间的最大长度,sum维护区间1…N中的最大连续区间长度,cover标志对应区间是否为空(没有住客)

 

具体实现过程:

BuildTree:建立一颗线段树,其中lsum,rsum,sum初始化为对应区间的长度

Query   :询问是否有长度为a的连续区间,如果有,返回对应区间的最左边的断点

UpData  :更新线段树的信息

PushUp  :将左右子区间合并

PushDown :标志向下传,延迟标志,简单来说就是先标志,然后等到下次询问或者更新时再去更新线段树


#include <stdio.h>

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1

const int maxn = 50000;

int n, mNum, op, a, b;
int sum[maxn*3], lsum[maxn*3], rsum[maxn*3], cover[maxn*3];

void BuildTree(int l, int r, int rt)
{
    cover[rt] = -1;
    lsum[rt] = rsum[rt] = sum[rt] = r-l+1;
    if (l == r)
        return ;
    
    int m = (l+r)>>1;
    BuildTree(lson);
    BuildTree(rson);
}/* BuildTree */

void PushDown(int rt, int k)
{
    if (cover[rt] != -1)
    {   /* Lazy Tag */
        cover[rt<<1] = cover[rt<<1|1] = cover[rt];
        lsum[rt<<1] = rsum[rt<<1] = sum[rt<<1] = cover[rt] ? 0:(k-(k>>1)); 
        lsum[rt<<1|1] = rsum[rt<<1|1] = sum[rt<<1|1] = cover[rt] ? 0:(k>>1);
        cover[rt] = -1;
    }
}/* PushDown */

int query(int w, int l, int r, int rt)
{
    if (l == r)
        return 1;
    
    PushDown(rt, r-l+1);    /* Push Down */
    
    int m = (l+r)>>1;
    if (sum[rt<<1] >= w)    /* 左连续区间长度 */ 
        return query(w, lson);
    else if (rsum[rt<<1]+lsum[rt<<1|1] >= w) /* 左区间后半部分与右区间左半部分长度 */ 
        return m-rsum[rt<<1]+1;
    else                    /* 右连续区间长度 */ 
        return query(w, rson);
}/* query */

int Max(int x, int y)
{
    return (x>y ? x:y);
}/* Max */

void PushUp(int rt, int k)
{
    lsum[rt] = lsum[rt<<1];   /* 左区间的左半部分 */ 
    rsum[rt] = rsum[rt<<1|1]; /* 右区间的右半部分 */ 
    
    if (lsum[rt] == k-(k>>1))
        lsum[rt] += lsum[rt<<1|1];
    if (rsum[rt] == k>>1)
        rsum[rt] += rsum[rt<<1];
    
    sum[rt] = Max(rsum[rt<<1]+lsum[rt<<1|1], Max(sum[rt<<1], sum[rt<<1|1]));
}/* PushUp */

void UpData(int L, int R, int c, int l, int r, int rt)
{
    if (L<=l && r<=R)
    {
        lsum[rt] = rsum[rt] = sum[rt] = c ? 0 : r-l+1;
        cover[rt] = c;
        
        return ;
    }/* End of If */
    
    PushDown(rt, r-l+1);    /* Push Down */
    
    int m = (l+r)>>1;
    if (L <= m)
        UpData(L, R, c, lson);
    if (R > m)
        UpData(L, R, c, rson);
    
    PushUp(rt, r-l+1);      /* Push Up */
}/* Updata */

int main()
{
    scanf("%d %d", &n, &mNum);
    
    BuildTree(1, n, 1); /* BuildTree */ 
    for (int i=1; i<=mNum; ++i)
    {
        scanf("%d", &op);
        if (op == 1)
        {   // Request
            scanf("%d", &a);
            if (sum[1] < a)
                printf("0\n");  /* No result */
            else
            {
                int pos = query(a, 1, n, 1);
                printf("%d\n", pos);
                UpData(pos, pos+a-1, 1, 1, n, 1);   /* UpData the interval */
            }
        }/* End of If */
        else
        {
            scanf("%d %d", &a, &b);
            UpData(a, a+b-1, 0, 1, n, 1);   /* UpData the interval */
        }
    }/* End of For */
    
    return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值