POJ-3225 Help with Intervals-线段树/成段更新+倍增区间法

题意:给一个0-65535的区间,区间端点为L,R 区间有开区间闭区间之分,一开始你拥有一个S区间,为全空的区间

n次操作,每次操作有4种,

U: 把T区间L,R覆盖成1 (S U T)

I:把求给出T区间,求S和T交集,也就是把[-∞,l)(r,∞]覆盖成0

D:用原区间S减去所给区间T,也就是把 区间T置为零

C:用所给区间T减去S,然后把结果赋值给S,也就是把把[-∞,l)(r,∞]覆盖成0,把【l,r】取反

S:求异或,直接把区间【l,r】取反


上面的操作都可以用线段树的区间更新完成,分别是一个set操作,和一个求异或的操作,我们下放延迟标记的时候,注意分清先后,先下放set操作,并且set操作之后需要清空异或标记。

对于异或操作,如果当前节点有set标记,那么只需要把set标记取反即可,如果没有set标记则取反异或标记

还有一个很关键的问题是开闭区间的问题,我们用倍增区间法来解决此问题

(l, r) --> [2 * l + 1, 2 * r - 1]
(l, r] --> [2 * l + 1, 2 * r]
[l, r) --> [2 * l, 2 * r - 1]
[l, r] --> [2 * l, 2 *r]

我们用偶数点代表整数点,奇数点代表两个整数点之间的 所有非整数点,那么问题便解决了.

直接把端点乘2,左开则l++,右开则r--, 处理完后  最后输出 的端点为 l/2 (r+1)/2   如果l或r为奇数,对应开区间,反之闭区间



<span style="font-size:14px;">#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
#define ptf(ar1,ar2)  printf("%d:%d\n",ar1,ar2);
typedef __int64 ll;
const ll maxn = 131570+500;
int vis[131570+500]; 
struct tree
{
	int  set[maxn*4],yihuo[maxn*4];
	void pd_set(int i,int l,int r)
	{ 
		if (set[i]!=-1)
		{
			set[i<<1]=set[i<<1|1]=set[i]; 
			yihuo[i<<1]=yihuo[i<<1|1]=yihuo[i]=0; 
			set[i]=-1;
		} 
		if (yihuo[i])
		{
			if (set[i<<1]!=-1) set[i<<1]^=1;
			else 	yihuo[i<<1]^=1;
			if (set[i<<1|1]!=-1) set[i<<1|1]^=1;
			else	yihuo[i<<1|1]^=1;
			yihuo[i]=0;  
		}
	}
	void update(int l,int r,int ql,int qr,int i,int val,int op)
	{
		if (ql>r||qr<l) return ;
		if (ql<=l&&qr>=r)
		{ 
			if (op)
			{
				if (set[i]!=-1) set[i]^=1;
				else 	yihuo[i]^=1; 
			}
			else
			{
				set[i]=val; 
				yihuo[i]=0;
			}
			return ;
		}
		pd_set(i,l,r); 
		int m=(l+r)>>1;
		update(l,m,ql,qr,i<<1,val,op);
		update(m+1,r,ql,qr,i<<1|1,val,op); 
	} 
	void init()
	{
		//	memset(sum,0,sizeof(sum));
		//	memset(yihuo,0,sizeof(yihuo));
		//	memset(set,-1,sizeof(set));
	}
	int query2( int l,int r,int i)
	{ 
		if  (l==r)
		{
			if (set[i]==1) vis[r]=1;
			return 0;
		}
		pd_set(i,l,r);
		int mid=(l+r)>>1;
		query2(l,mid,i<<1); 
		query2(mid+1,r,i<<1|1);
	}
	
};

tree tp ;
int main(  )
{
    char op;
    int l,r;
    char lrat,rrat;
    tp.init();
	while(scanf("%c %c%d,%d%c",&op,&lrat,&l,&r,&rrat)!=EOF)
    {
		getchar();
        l*=2;
        r*=2;
        if (lrat=='(') l++;
        if (rrat==')') r--;
        if (op=='U')
            tp.update(0,131570,l,r,1,1,0);
        if (op=='I') 
        {
            tp.update(0,131570,0,l-1,1,0,0);
            tp.update(0,131570,r+1,131570,1,0,0);
        }
        if (op=='D')
            tp.update(0,131570,l,r,1,0,0);
        if (op=='C')
        {
            tp.update(0,131570,0,l-1,1,0,0);
            tp.update(0,131570,r+1,131570,1,0,0);
            tp.update(0,131570,l,r,1,1,1);
			
        }
        if (op=='S')
            tp.update(0,131570,l,r,1,1,1);
		
    }
	int i;
	tp.query2(0,131570 ,1);
	
    int line=0;
    int exist=0;
    for(  i=0; i<=131570 ; i++)
    {
		
        if (vis[i])
        {
            exist=1;
            if (line)  printf(" ");
            int j=i+1;
            while(vis[j]&&j<=131570) j++;
            j--;
            if (i%2) printf("(");
            else printf("[");
			
            printf("%d,%d",i/2,(j+1)/2);
            if (j%2)
                printf(")");
            else
                printf("]");
            line=1;
            i=j;
        }
    }
	if (exist==0)
		printf("empty set");
	
	printf("\n");
	return 0;
}
</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值