[BZOJ1018] [SHOI2008]堵塞的交通traffic(线段树维护连通性)

本文介绍了一道关于线段树的经典问题,利用线段树维护二维平面上的连通性。通过定义特殊的结构体来记录每个节点的连通状态,并采用合并策略更新线段树,最终解决询问和修改操作。

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

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1018


之前挖的坑,现在填吧。
之前一眼看觉得是LCT,一看时限 3s ????
n非常大是log级别的,并查集肯定不可行,
貌似时限开大也不能用lct因为不是树?(也许是我弱

那么就滚去膜题解了了。
这题其实是线段树维护连通性的经典问题。
突破点就在于只有两行。
那么我们只要以每一列为一个节点开一个线段树,对于每一个节点开一个结构体来维护这个节点的连通性即可。 那么如何维护呢?
我们开一个数组,h[2][2],记录的就是这个节点的左端点的第0/1行何右端点的第0/1行是否连通。
然后在记录一下这个节点左端点的上下两行(也就是竖着)是否联通和右端点的上下两行是否联通,
还要在线段树维护两个孩子之间的部分是否联通。
合并的话有几种情况,直接连通或者绕一个弯或者绕两个弯
这里写图片描述
(自幼灵魂画手在此报道!)

修改有两种情况,同一行相邻格子,或者同一列的两个格子,大体也差不多,yy一下就行了,注意时时合并。

询问函数部分就和线段树一样。
但是主函数询问的时候注意有这种情况:
这里写图片描述
也就是说连通性不一定是在两点之间的,有可能绕到了前面或者后面去,那么我们询问的时候就要问一下前面和后面的情况在做一个整合即可。

至今做过最有趣的题目之一。


code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node//维护一个端点的连通性
{
    bool lb,rb;//竖
    bool h[2][2];//维护从x节点最左端的i行能否到最右端的j行
    node()
    {
        lb=rb=0;
        memset(h,0,sizeof(h));
    }
};
struct trnode//线段树
{
    int l,r;
    int lc,rc;
    node f;
    bool tp,bm;//维护两个孩子之间的部分(mid~mid+1)
}tr[410000];
int trlen;
int build(int l,int r)
{
    int t=++trlen;
    tr[t].l=l;tr[t].r=r;
    tr[t].tp=tr[t].bm=0;
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[t].lc=build(l,mid);
        tr[t].rc=build(mid+1,r);
    }
    else tr[t].f.h[0][0]=tr[t].f.h[1][1]=1;
    return t;
}
node merg(node a,int tp,int bm,node b)
{
    node c;
    c.lb=a.lb || (a.h[0][0] && tp && b.lb && bm && a.h[1][1]);//直接连通或者绕一个弯 
    c.rb=b.rb || (b.h[0][0] && tp && a.rb && bm && b.h[1][1]);
    for(int i=0;i<=1;i++)
        for(int j=0;j<=1;j++)
        {
            c.h[i][j] =a.h[i][0] && tp && b.h[0][j];//绕两个弯 
            c.h[i][j]|=a.h[i][1] && bm && b.h[1][j];
        }
    return c;
}

void change(int x,int x1,int y1,int x2,int y2,bool b)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    int mid=(tr[x].l+tr[x].r)/2;

    if(x1==x2 && y1==mid)//横着,正好中间
    {
        if(x1==0) tr[x].tp=b;
        else tr[x].bm=b;
        tr[x].f=merg(tr[lc].f,tr[x].tp,tr[x].bm,tr[rc].f);
        return;
    }
    if(tr[x].l==tr[x].r)//其他单点
    {
        tr[x].f.h[0][1]=tr[x].f.h[1][0]=tr[x].f.lb=tr[x].f.rb=b;
        return;
    }

    if(y1<=mid) change(lc,x1,y1,x2,y2,b);
    else change(rc,x1,y1,x2,y2,b);
    tr[x].f=merg(tr[lc].f,tr[x].tp,tr[x].bm,tr[rc].f);
}
node ask(int x,int l,int r)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    int mid=(tr[x].l+tr[x].r)/2;
    if(tr[x].l==l && tr[x].r==r) return tr[x].f;

    if(r<=mid) return ask(lc,l,r);
    if(l>mid) return ask(rc,l,r);
    return merg(ask(lc,l,mid),tr[x].tp,tr[x].bm,ask(rc,mid+1,r));
}
char ss[20];
int main()
{
    int n;scanf("%d",&n);
    trlen=0;build(1,n);
    while(1)
    {
        scanf("%s",ss);
        if(ss[0]=='E') break;
        int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x1--;x2--;
        if(y1>y2) swap(x1,x2),swap(y1,y2);
        if(ss[0]=='A')
        {
            node a,b,c;
            a=ask(1,1,y1);b=ask(1,y1,y2);c=ask(1,y2,n);
            if(a.rb) b.lb=1;
            if(c.lb) b.rb=1;

            bool bk=0;
            for(int i=0;i<=1;i++)
                for(int j=0;j<=1;j++)
                    if(b.h[i][j])
                        if( (i==x1 || b.lb)&&(j==x2 || b.rb) )
                            bk=1;
            if(bk) printf("Y\n");
            else printf("N\n");
        }
        if(ss[0]=='O') change(1,x1,y1,x2,y2,1);
        if(ss[0]=='C') change(1,x1,y1,x2,y2,0);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值