bzoj1018: [SHOI2008]堵塞的交通traffic【线段树维护连通性】

Description

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

Input

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

Output

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

Sample Input

2

Open 1 1 1 2

Open 1 2 2 2

Ask 1 1 2 2

Ask 2 1 2 2

Exit

Sample Output

Y

N

解题思路:

又是线段树的神奇应用:维护2*n网格图的连通性。

先看官方题解:
假设询问(r1,1)到(r2,2)(c2>=c1)的连通性,我们对最复杂的连通方式进行分析:
这里写图片描述
将路线分成3个部分,很容易发现,路线只有几种可能:
1.从(r1,1)直接一直向右、上、下走直接到(r2,2)
2.先从(r1,1)到(r1,2),再从(r1,2)直接一直向右、上、下走直接到(r2,2)
3.从(r1,1)直接一直向右、上、下走直接到(r2,1),再从(r2,1)到(r2,2)
4.先从(r1,1)到(r1,2),再从(r1,2)直接一直向右、上、下走直接到(r2,1) ,再从(r2,1)到(r2,2)
其中,(r1,1)到(r1,2)的方法只能是一直向左走到某个地方,然后想下,再一直向右走到(r1,2)
同理,(r2,1)到(r2,2)的方法只能是一直向右走到某个地方,然后想下,再一直向左走到(r2,2)
于是我们只需要能够快速得到如下数据便可以判断(r1,1),(r2,2)是否连通:
1.(r1,1)到(r1,2),只能先左走一段,再向下,再右走一段是否有路
2.(r2,1)到(r2,2),只能先右走一段,再向下,再左走一段是否有路
3.从(r1,1)到(r2,1),从(r1,1)到(r2,2),从(r1,2)到(r2,1),从(r1,2)到(r2,2),只能右上下走,是否有路
而这3个数据都可以用线段树维护,具体的维护方法请自行思考。

我说一下具体的维护方法:
线段树一个节点(l,r)要维护题解中所说的共六种连通性,且只能经过区间(l,r)内的点,查询时要查(1,c1),(c1,c2),(c2,n)三个区间的联通情况,再作讨论得到答案。
具体细节还是要看代码,比较好懂。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=100005;
const int dx[3]={-1,0,1};
const int dy[3]={0,1,0};
struct node{bool s[2],x[2],v[2];}tr[N<<2];
int n,r1,r2,c1,c2;
bool a[N][2][2];

void build(int k,int l,int r)
{
    if(l==r){tr[k].x[0]=tr[k].x[1]=1;return;}
    int mid=l+r>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
}

void update(node &u,node l,node r,int mid)
{
    u.s[0]=(l.s[0])||(l.x[0]&&a[mid][0][0]&&r.s[0]&&a[mid][1][0]&&l.x[1]);//左上左下 
    u.s[1]=(r.s[1])||(r.x[0]&&a[mid][0][0]&&l.s[1]&&a[mid][1][0]&&r.x[1]);//右上右下 
    u.x[0]=(l.x[0]&&a[mid][0][0]&&r.x[0])||(l.v[0]&&a[mid][1][0]&&r.v[1]);//左上右上 
    u.x[1]=(l.x[1]&&a[mid][1][0]&&r.x[1])||(l.v[1]&&a[mid][0][0]&&r.v[0]);//左下右下 
    u.v[0]=(l.x[0]&&a[mid][0][0]&&r.v[0])||(l.v[0]&&a[mid][1][0]&&r.x[1]);//左上右下 
    u.v[1]=(l.x[1]&&a[mid][1][0]&&r.v[1])||(l.v[1]&&a[mid][0][0]&&r.x[0]);//左下右上 
}

void modify(int k,int l,int r,int pos)
{
    if(l==r)
    {
        tr[k].s[0]=tr[k].s[1]=tr[k].v[0]=tr[k].v[1]=a[pos][0][1];
        tr[k].x[0]=tr[k].x[1]=1;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid)modify(k<<1,l,mid,pos);
    else modify(k<<1|1,mid+1,r,pos);
    update(tr[k],tr[k<<1],tr[k<<1|1],mid);
}

void change(int f)
{
    if(r1>r2)swap(r1,r2),swap(c1,c2);
    int i;
    for(i=0;i<3;i++)
        if(r1+dy[i]==r2&&c1+dx[i]==c2)break;
    if(i==0)a[c2][r2][0]=f,modify(1,1,n,c2);
    else if(i==1)a[c2][0][1]=f,modify(1,1,n,c2);
    else a[c1][r1][0]=f,modify(1,1,n,c1);
}

node query(int k,int l,int r,int x,int y)
{
    if(l==x&&r==y)return tr[k];
    int mid=l+r>>1;
    if(y<=mid)return query(k<<1,l,mid,x,y);
    else if(x>mid)return query(k<<1|1,mid+1,r,x,y);
    node a=query(k<<1,l,mid,x,mid),b=query(k<<1|1,mid+1,r,mid+1,y),res;
    update(res,a,b,mid);
    return res;
}

void get_ans()
{
    if(c1>c2)swap(r1,r2),swap(c1,c2);
    node l=query(1,1,n,1,c1),mid=query(1,1,n,c1,c2),r=query(1,1,n,c2,n);
    if(r1==r2)
    {
        if(r1==0)
        {
            if(mid.x[0]||(l.s[1]&&mid.v[1])||(mid.v[0]&&r.s[0])||(l.s[1]&&mid.x[1]&&r.s[0]))puts("Y");
            else puts("N");
        }
        else
        {
            if(mid.x[1]||(l.s[1]&&mid.v[0])||(mid.v[1]&&r.s[0])||(l.s[1]&&mid.x[0]&&r.s[0]))puts("Y");
            else puts("N");
        }
    }
    else
    {
        if(r1==0)
        {
            if(mid.v[0]||(l.s[1]&&mid.x[1])||(mid.x[0]&&r.s[0])||(l.s[1]&&mid.v[1]&&r.s[0]))puts("Y");
            else puts("N");
        }
        else 
        {
            if(mid.v[1]||(l.s[1]&&mid.x[0])||(mid.x[1]&&r.s[0])||(l.s[1]&&mid.v[0]&&r.s[0]))puts("Y");
            else puts("N");
        }
    }
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint();build(1,1,n);
    char s[5];
    while(scanf("%s",s))
    {
        if(s[0]=='E')break;
        r1=getint()-1,c1=getint(),r2=getint()-1,c2=getint();
        if(s[0]=='O')change(1);
        else if(s[0]=='C')change(0);
        else get_ans();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值