1018: [SHOI2008]堵塞的交通traffic

1018: [SHOI2008]堵塞的交通traffic

Time Limit: 3 Sec   Memory Limit: 162 MB
Submit: 3166   Solved: 1056
[ Submit][ Status][ Discuss]

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

HINT

Source

[ Submit][ Status][ Discuss]

第一次看到用线段树维护连通性的题。。。

直接从最麻烦的情况入手好了,因为能解决最麻烦问题的程序总是能解决较简单的情形的

假如是从A走到D,不妨假设总有ca < cd

只考虑图示矩形内部的边的话,A到D的路径只能经过往右往上往下三种方向的边

这样的信息线段树显然可以直接维护,一个矩形可以用线段树上logn个节点表示,

于是,回答的时候只要两两之间再判一下连通性即可

不过这样显然没考虑完全所有情况

考虑图中的点B和C,假如A,D不能直接用这种简单的方法连通,那就一定得用这两个点辅助了

比如如果想要用A左边的边,那一定会在再次进入矩形的时候经过B

因为如果回来不经过B,那。。。。。为什么要走出去啊?

因此,A到B的路径是A往左一段接着往下然后一直往右

同理,C到D时往右往下往左

综合以上信息,就能轻易看出ABCD四个点之间的连通性,问题也就解决了


反正就是各种分类讨论,线段树总是能维护,挺码农的(可能是因为我姿势丑?)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
 
const int maxn = 1E5 + 10;
const int T = 4;
const int INF = ~0U>>1;
 
struct data{
    bool f0,f1,f2,f3; data(){f0 = f1 = f2 = f3 = 0;}
}f[maxn*T];
 
int n,tp,stk[maxn],L[maxn]; char s[10];
bool E[2][maxn][3],ug[maxn*T],dg[maxn*T],h[maxn*T];
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int getcom()
{
    scanf("%s",s + 1);
    if (s[1] == 'C') return 1;
    if (s[1] == 'O') return 2;
    if (s[1] == 'A') return 3;
    return 4;
}
 
data Merge(const data &A,const data &B,const int &l)
{
    data r1,r2; bool *e0 = E[0][l],*e1 = E[1][l]; 
    if ((A.f0 && e0[0]) || (A.f1 && e1[0] && e1[2])) r1.f0 = 1;
    if ((A.f1 && e1[0]) || (A.f0 && e0[0] && e0[2])) r1.f1 = 1;
    if ((A.f2 && e0[0]) || (A.f3 && e1[0] && e1[2])) r1.f2 = 1;
    if ((A.f3 && e1[0]) || (A.f2 && e0[0] && e0[2])) r1.f3 = 1;
    if ((r1.f0 && B.f0) || (r1.f1 && B.f2)) r2.f0 = 1;
    if ((r1.f0 && B.f1) || (r1.f1 && B.f3)) r2.f1 = 1;
    if ((r1.f2 && B.f0) || (r1.f3 && B.f2)) r2.f2 = 1;
    if ((r1.f2 && B.f1) || (r1.f3 && B.f3)) r2.f3 = 1;
    return r2;
}
 
void Modify(int o,int l,int r,int pos)
{
    if (l == r) {h[o] = E[0][l][2]; return;}
    int mid = (l + r) >> 1;
    if (pos <= mid) Modify(o<<1,l,mid,pos);
    else Modify(o<<1|1,mid+1,r,pos);
    f[o] = Merge(f[o<<1],f[o<<1|1],mid+1);
    ug[o] = (ug[o<<1] && ug[o<<1|1] && E[0][mid][1]) ? 1 : 0;
    dg[o] = (dg[o<<1] && dg[o<<1|1] && E[1][mid][1]) ? 1 : 0;
    h[o] = (h[o<<1] || h[o<<1|1]) ? 1 : 0;
}
 
void Add_block(int o,int l,int r,int ql,int qr)
{
    if (ql <= l && r <= qr) {stk[++tp] = o; L[tp] = l; return;}
    int mid = (l + r) >> 1;
    if (ql <= mid) Add_block(o<<1,l,mid,ql,qr);
    if (qr > mid) Add_block(o<<1|1,mid+1,r,ql,qr);
}
 
int Query_Right(int o,int l,int r,int ql,int qr)
{
    if (!h[o]) return 0; if (l == r) return l;
    int mid = (l + r) >> 1,ret = 0;
    if (ql <= l && r <= qr)
    {
        if (h[o<<1|1]) return Query_Right(o<<1|1,mid+1,r,ql,qr);
        if (h[o<<1]) return Query_Right(o<<1,l,mid,ql,qr);
        return 0;
    }
    if (qr > mid) ret = Query_Right(o<<1|1,mid+1,r,ql,qr);
    return ret ? ret : Query_Right(o<<1,l,mid,ql,qr);
}
 
int Query_Left(int o,int l,int r,int ql,int qr)
{
    if (!h[o]) return INF; if (l == r) return l;
    int mid = (l + r) >> 1,ret = INF;
    if (ql <= l && r <= qr)
    {
        if (h[o<<1]) return Query_Left(o<<1,l,mid,ql,qr);
        if (h[o<<1|1]) return Query_Left(o<<1|1,mid+1,r,ql,qr);
        return INF;
    }
    if (ql <= mid) ret = Query_Left(o<<1,l,mid,ql,qr);
    return ret != INF ? ret : Query_Left(o<<1|1,mid+1,r,ql,qr);
}
 
bool Connect(int l,int r)
{
    tp = 0; Add_block(1,1,n,l,r);
    for (int i = 2; i <= tp; i++)
    {
        if (!ug[stk[i]] || !dg[stk[i]]) return 0;
        if (!E[0][L[i]][0] || !E[1][L[i]][0]) return 0;
    }
    return (ug[stk[1]] & dg[stk[1]]);
}
 
bool Ask_Left(int c)
{
    if (E[0][c][2]) return 1;
    if (c > 1)
    {
        int pos = Query_Right(1,1,n,1,c - 1);
        if (!pos) return 0;
        return Connect(pos,c);
    }
    return 0;
}
 
bool Ask_Right(int c)
{
    if (E[1][c][2]) return 1;
    if (c < n)
    {
        int pos = Query_Left(1,1,n,c + 1,n);
        if (pos == INF) return 0;
        return Connect(c,pos);
    }
    return 0;
}
 
bool Ask(int r1,int c1,int r2,int c2,bool flag0,bool flag1)
{
    tp = 0; Add_block(1,1,n,c1,c2); data now = f[stk[1]];
    for (int i = 2; i <= tp; i++) now = Merge(now,f[stk[i]],L[i]);
    if (!r1 || flag0)
    {
        if (now.f0 && (!r2 || flag1)) return 1;
        if (now.f1 && (r2 || flag1)) return 1;
    }
    if (r1 || flag0)
    {
        if (now.f2 && (!r2 || flag1)) return 1;
        if (now.f3 && (r2 || flag1)) return 1;
    }
    return 0;
}
 
void Build(int o,int l,int r)
{
    if (l == r)
    {
        f[o].f0 = f[o].f3 = 1;
        ug[o] = dg[o] = 1; return;
    }
    int mid = (l + r) >> 1;
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); Build(1,1,n);
    for (;;)
    {
        int com = getcom(),r1,c1,r2,c2;
        if (com == 1)
        {
            r1 = getint(); c1 = getint(); --r1;
            r2 = getint(); c2 = getint(); --r2;
            if (c1 > c2) swap(r1,r2),swap(c1,c2);
            if (c1 == c2) E[0][c1][2] = E[1][c2][2] = 0;
            else E[r1][c1][1] = E[r2][c2][0] = 0;
            Modify(1,1,n,c1); if (c1 != c2) Modify(1,1,n,c2);
        }
        else if (com == 2)
        {
            r1 = getint(); c1 = getint(); --r1;
            r2 = getint(); c2 = getint(); --r2;
            if (c1 > c2) swap(r1,r2),swap(c1,c2);
            if (c1 == c2) E[0][c1][2] = E[1][c1][2] = 1;
            else E[r1][c1][1] = E[r2][c2][0] = 1;
            Modify(1,1,n,c1); if (c1 != c2) Modify(1,1,n,c2);
        }
        else if (com == 3)
        {
            r1 = getint(); c1 = getint(); --r1;
            r2 = getint(); c2 = getint(); --r2;
            if (c1 > c2) swap(r1,r2),swap(c1,c2);
            if (c1 == c2)
            {
                if (Ask_Left(c1)) {puts("Y"); continue;}
                if (Ask_Right(c1)) {puts("Y"); continue;}
                puts("N");
            }
            else
            {
                bool f0 = Ask_Left(c1),f1 = Ask_Right(c2);
                if (Ask(r1,c1,r2,c2,f0,f1)) puts("Y"); else puts("N");
            }
        }
        else break;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值