「BZOJ3226」[SDOI2008] 校门外的区间(线段树好题)

本文详细介绍了如何使用线段树解决区间操作问题,通过拆点思想将区间转化为点的操作,实现了包括标记、反转等复杂功能。并提供了一个完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

传送门
这里写图片描述

题解

参考了黄学长的blog
想到用暴力做标记,发现了区间的操作,并且把区间左右端点的开与闭进行了保存,但是却没有想到线段树也没有上升为拆点的思想。
这里,我们把(1,2)区间拆成三个点——左端点,右端点和区间,如果是[1,2]的话就相当于拆成了一个点。看下图的拆点过程。
这里写图片描述
首先把每个点“分散”一下,然后把线段向右平移两个单位(为了为0留出位置),原来的每个点就代表的一个闭区间。
然后就进行区间的操作。

  • U 区间标记1
  • I 两边区间标记0
  • D 区间标记1
  • C 区间两边标记0,中间取反
  • S 区间取反
    然后时候线段树操作

输出看代码吧

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
const int inf=1e9;
const int n=65535<<2|1;

int x,y;
struct Tree{
    int l,r,tag,rev,col;//tag标记颜色 rev是否进行反转 
}tree[n<<3];

int read()//拆点 
{
    char ch=getchar();int x=0,f=0;
    while (ch<'0'||ch>'9') {if (ch=='(') f=1; ch=getchar();}
    while (ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();
    }
    if (ch==')') f=-1;
    x<<=1; x+=f+2;
    return x;//x<<1+f+2 位运算的优先级比任何都低 
}

void build_tree(int l,int r,int rt)
{
    tree[rt].l=l; tree[rt].r=r; tree[rt].tag=-1;
    if (l==r) return;
    int mid=(l+r)>>1;
    build_tree(l,mid,rt<<1);
    build_tree(mid+1,r,rt<<1|1); 
}

void pushdown(int rt)
{
    int tag=tree[rt].tag,rev=tree[rt].rev;
    tree[rt].tag=-1; tree[rt].rev=0;
    if (tree[rt].l==tree[rt].r)
    {
        if (tag!=-1) tree[rt].col=tag;
        tree[rt].col^=rev;//tag的优先级大于rev
        return;
    }
    if (tag!=-1)
    {
        tree[rt<<1].tag=tree[rt<<1|1].tag=tag;
        tree[rt<<1].rev=tree[rt<<1|1].rev=0;
    }
    tree[rt<<1].rev^=rev; tree[rt<<1|1].rev^=rev;
}

void change(int rt,int x,int y,int C)
{
    if (x>y) return;
    int l=tree[rt].l,r=tree[rt].r,mid=(l+r)>>1;
    pushdown(rt);
    if (l==x && r==y)
    {
        if (C==-1) tree[rt].rev^=1;
        else tree[rt].tag=C;
        return;
    }
    if (y<=mid) change(rt<<1,x,y,C);//重新选择区间 
    else if (x>mid) change(rt<<1|1,x,y,C); 
    else//分治 
    {
        change(rt<<1,x,mid,C);
        change(rt<<1|1,mid+1,y,C);
    }
}

int ques(int rt,int x)
{
    pushdown(rt);//询问前下传标记 
    int l=tree[rt].l,r=tree[rt].r;
    if (l==r) return tree[rt].col;
    int mid=(l+r)>>1;
    if (x<=mid) return ques(rt<<1,x);
    else return ques(rt<<1|1,x);
}

int main()
{
//  freopen("interval.in","r",stdin);
//  freopen("interval.out","w",stdout);
    char opt[5];
    build_tree(1,n,1);
    while (scanf("%s",opt)!=EOF)
    {
        x=read(); y=read();
        if (opt[0]=='U') change(1,x,y,1);
        else if (opt[0]=='I') {change(1,1,x-1,0); change(1,y+1,n,0);}
        else if (opt[0]=='D') change(1,x,y,0);
        else if (opt[0]=='C') {change(1,1,x-1,0); change(1,y+1,n,0); change(1,x,y,-1);}
        else if (opt[0]=='S') change(1,x,y,-1);//S区间取反 
    }
    //shuchu
        int start=-1,last=-1,flag=0;
    for(int i=1;i<=n;i++)
        if(ques(1,i))
        {
            if(start==-1)start=i;
            last=i;
        }
        else 
        {
            if(start!=-1)
            {
                if(flag)printf(" ");
                else flag=1;
                if(start&1)printf("(");
                else printf("[");
                printf("%d",start/2-1);
                printf(",");
                printf("%d",(last+1)/2-1);
                if(last&1)printf(")");
                else printf("]");
            }
            last=start=-1;
        }
    if(!flag)puts("empty set");
    return 0;
    int l=-1,r=-1; bool ifl=0,fir=0;//是否已经找到 
    for (int i=1; i<=n; i++)
    {
        if (ques(1,i))
            if (!ifl) {l=i; ifl=1;}
        else if (ifl) {r=i; break;}
    }
    if (!ifl) {printf("empty set"); return 0;}
    char ansl,ansr;
    ansl=(l&1)?'(':'['; ansr=(r&1)?')':']';
    l=(l&1)?(l-3)>>1:(l-2)>>1; r=(r&1)?(r-1)>>1:(r-2)>>1;
    printf("%c%d,%d%c ",ansl,l,r,ansr);
    return 0;
}
/*
U [1,5]
D [3,3]
S [2,4]
C (1,5)
I (2,3]

(2,3) 每个区间后面带一个空格
*/

总结

好好学习线段树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值