洛谷 P4246 [SHOI2008]堵塞的交通 线段树

本文介绍了一个基于段树的数据结构实现的查询系统,用于处理一个由2行C列矩形网格构成的小人国交通网络中道路的通断变化,并据此判断任意两点间是否存在路径连通。

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

题目描述
有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可以被看成是一个22C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C2C个城市和3C23C−2条道路。
小人国的交通状况非常槽糕。有的时候由于交通堵塞,两座城市之间的道路会变得不连通,直到拥堵解决,道路才会恢复畅通。初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入膏肓的小人国交通系统。 小人国的交通部将提供一些交通信息给你,你的任务是根据当前的交通情况回答查询的问题。交通信息可以分为以下几种格式:
Close r1 c1 r2 c2Close r1 c1 r2 c2:相邻的两座城市(r1,c1)(r1,c1)(r2,c2)(r2,c2)之间的道路被堵塞了;
Open r1 c1 r2 c2Open r1 c1 r2 c2:相邻的两座城市(r1,c1)(r1,c1)(r2,c2)(r2,c2)之间的道路被疏通了;
Ask r1 c1 r2 c2Ask r1 c1 r2 c2:询问城市(r1,c1)(r1,c1)(r2,c2)(r2,c2)是否连通。如果存在一条路径使得这两条城市连通,则返回Y,否则返回N。
注:riri表示行数,cici表示列数,1ri2,1ciC1≤ri≤2,1≤ci≤C
输入输出格式
输入格式:

第一行只有一个整数CC,表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行Exit作为结束。我们假设在一开始所有的道路都是堵塞的。我们保证C小于等于100000100000,信息条数小于等于 100000100000

输出格式:

对于每个查询,输出一个Y或N。

输入输出样例
输入样例#1:
2
Open 1 1 1 2
Open 1 2 2 2
Ask 1 1 2 2
Ask 2 1 2 2
Exit
输出样例#1:
Y
N
说明
数据范围:
对于100%的数据,
1C1000001≤C≤10000011≤信息条数 100000≤100000

分析:我们维护只走在纵坐标为[l,r][l,r]为矩形内的边时,矩形四角的连通性。合并区间还是很好维护的,因为合并两个区间时需要midmidmid+1mid+1这两条边的连通性,开一个数组记录一下就好了,具体可以看代码。
对于查询从(x,y)(x,y)(l,r)(l,r)连通性,有44种情况,我们设(!x,y)(x,y)(x,y)在第yy列的另一个点。
第一种是直接从[y,r]内矩阵的边走过去;
第二种是先从[1,y][1,y]的矩阵从(x,y)(x,y)走到(!x,y)(!x,y),然后再从[y,r][y,r]的矩阵走到(l,r)(l,r)
第三种是从[y,r][y,r]的矩阵从(x,y)(x,y)走到(!l,r)(!l,r),再从[r,n][r,n]的矩阵走到(l,r)(l,r)
第四种是先从[1,y][1,y]的矩阵从(x,y)(x,y)走到(!x,y)(!x,y),然后再从[y,r][y,r]的矩阵走到(!l,r)(!l,r),再从[r,n][r,n]的矩阵走到(l,r)(l,r)

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=1e5+7;

using namespace std;

struct node{
    int a[4][4];
    int l,r;
}t[maxn*4];

int n,a,b,c,d;
int g[maxn*3];
char s[10];

node merge(node x,node y)
{
    node z;
    int r=x.r;
    z.a[0][1]=z.a[1][0]=(x.a[0][1]&g[r]&y.a[0][1])|(x.a[0][3]&g[n+r]&y.a[2][1]);
    z.a[0][2]=z.a[2][0]=(x.a[0][2])|(x.a[0][1]&g[r]&y.a[0][2]&g[n+r]&x.a[3][2]);
    z.a[0][3]=z.a[3][0]=(x.a[0][1]&g[r]&y.a[0][3])|(x.a[0][3]&g[n+r]&y.a[2][3]);
    z.a[1][2]=z.a[2][1]=(x.a[2][1]&g[r]&y.a[0][1])|(x.a[2][3]&g[n+r]&y.a[2][1]);
    z.a[1][3]=z.a[3][1]=(y.a[1][3])|(y.a[1][0]&g[r]&x.a[1][3]&g[n+r]&y.a[2][3]);
    z.a[2][3]=z.a[3][2]=(x.a[2][1]&g[r]&y.a[0][3])|(x.a[2][3]&g[n+r]&y.a[2][3]);
    z.l=x.l;
    z.r=y.r;
    return z;
}

node neww(int l)
{
    node z;
    z.l=z.r=l;
    z.a[0][1]=z.a[1][0]=z.a[2][3]=z.a[3][2]=1;
    z.a[0][2]=z.a[2][0]=z.a[0][3]=z.a[3][0]=z.a[1][2]=z.a[2][1]=z.a[1][3]=z.a[3][1]=g[n*2+l];
    return z;
}

void change(int p,int l,int r,int x,int y)
{
    if (l==r)
    {
        t[p]=neww(l);
        return;
    }
    int mid=(l+r)/2;
    if (y<=mid) change(p*2,l,mid,x,y);
    else if (x>mid) change(p*2+1,mid+1,r,x,y);
    else
    {
        change(p*2,l,mid,x,mid);
        change(p*2+1,mid+1,r,mid+1,y);
    }
    t[p]=merge(t[p*2],t[p*2+1]);
}

node query(int p,int l,int r,int x,int y)
{
    if ((l==x) && (r==y)) return t[p];
    int mid=(l+r)/2;
    if (y<=mid) return query(p*2,l,mid,x,y);
    else
    {
        if (x>mid) return query(p*2+1,mid+1,r,x,y);
        else
        {
            return merge(query(p*2,l,mid,x,mid),query(p*2+1,mid+1,r,mid+1,y));
        }
    }
}

int main()
{
    scanf("%d",&n); 
    while (1)
    {
        scanf("%s",s);
        if (s[0]=='E') break;
        scanf("%d%d%d%d",&a,&b,&c,&d);      
        if (b>d) swap(a,c),swap(b,d);       
        if ((s[0]=='O') || (s[0]=='C'))
        {
            int k;
            if (s[0]=='O') k=1;
                      else k=0;
            if (b==d) g[n*2+b]=k;
            else
            {
                if (a==1) g[b]=k;
                     else g[n+b]=k;
            }
            change(1,1,n,b,d);
        }
        if (s[0]=='A')
        {           
            node ans=query(1,1,n,b,d);
            node lans=query(1,1,n,1,b);
            node rans=query(1,1,n,d,n);
            int num1=a*2-2,num2=c*2-1;
            int flag=0;
            if (ans.a[num1][num2]) flag=1;
            if (ans.a[num1^2][num2]&lans.a[1][3]) flag=1;
            if (ans.a[num1][num2^2]&rans.a[0][2]) flag=1;
            if(ans.a[num1^2][num2^2]&lans.a[1][3]&rans.a[0][2]) flag=1;
            if (flag) printf("Y\n");
                 else printf("N\n"); 
        }
    }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值