【BZOJ1018】【SHOI2008】堵塞的交通traffic(线段树)(好题)

本文介绍了一种针对小人国特殊布局的交通查询系统实现方法。系统利用线段树维护道路状态,支持道路堵塞、疏通操作及两点间连通性查询。通过巧妙的数据结构设计,确保了查询效率。

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

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

网站上的题解:
假设询问(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个数据都可以用线段树维护,具体的维护方法请自行思考。
/****************************************************/
这道题确实是一道非常好的题目,与通常做的线段树完全不一样,给了我许多新的思路,在这里再分享另外一篇博客,讲的更详细一些:
戳这里
我的代码如下:

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<string>
#define N 100005
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
struct nod{bool g[6];}s[5],tr[N*3];
int n;
bool mp[N*3];
int calc(int x,int y){return x*(n-1)+y;}
void init()
{
    scanf("%d",&n);
    s[0]=(nod){1,1,0,0,0,0};
    s[1]=(nod){1,1,1,1,1,1};
    memset(mp,0,sizeof(mp));
    memset(tr,0,sizeof(tr));
}
void build(int rt,int l,int r)
{
    if(l==r) 
    { 
        tr[rt]=s[0]; 
        return;
    }
    build(ls,l,mid);build(rs,mid+1,r); 
}
nod merge(nod a,nod b,bool x,bool y)
{
    nod c;
    c.g[0]=(a.g[0] && x && b.g[0])|(a.g[4] && y && b.g[5]);
    c.g[1]=(a.g[1] && y && b.g[1])|(a.g[5] && x && b.g[4]);
    c.g[2]=(a.g[2])|(a.g[0] && x && b.g[2] && y && a.g[1]);
    c.g[3]=(b.g[3])|(b.g[0] && x && a.g[3] && y && b.g[1]);
    c.g[4]=(a.g[0] && x && b.g[4])|(a.g[4] && y && b.g[1]);
    c.g[5]=(b.g[0] && x && a.g[5])|(b.g[5] && y && a.g[1]);
    return c;
}
void insert(int rt,int l,int r,int x1,int y1,int x2,int y2,bool c)
{
    int y=min(y1,y2);
    if(x1==x2 && y==mid) 
    {  
        mp[calc(x1,y)]=c; 
        tr[rt]=merge(tr[ls],tr[rs],mp[calc(0,mid)],mp[calc(1,mid)]); 
        return;
    }
    else if(x1!=x2 && l==r) 
    { 
        mp[2*(n-1)+y]=c; 
        tr[rt]=s[c];
        return;
    }
    if(y<=mid) insert(ls,l,mid,x1,y1,x2,y2,c);
    if(y>mid) insert(rs,mid+1,r,x1,y1,x2,y2,c);
    tr[rt]=merge(tr[ls],tr[rs],mp[calc(0,mid)],mp[calc(1,mid)]);
}
nod query(int rt,int l,int r,int L,int R)
{
     if(l>=L && r<=R) return tr[rt];
     if(R<=mid) return query(ls,l,mid,L,R);
     if(L>mid) return query(rs,mid+1,r,L,R);
     else return merge(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R),mp[calc(0,mid)],mp[calc(1,mid)]);
}
void ask(int x1,int y1,int x2,int y2)
{
    bool ans;
    if(y1>y2) swap(x1,x2),swap(y1,y2);
    s[2]=query(1,1,n,y1,y2);
    s[3]=query(1,1,n,1,y1);
    s[4]=query(1,1,n,y2,n);
    if(x1==x2) ans=(s[2].g[x1])|(s[3].g[3] && s[2].g[4+x1^1])|(s[4].g[2] && s[2].g[4+x1])|(s[3].g[3] && s[4].g[2] && s[2].g[x1^1]); 
    if(x1!=x2) ans=(s[2].g[4+x1])|(s[3].g[3] && s[2].g[x1^1])|(s[4].g[2] && s[2].g[x1])|(s[3].g[3] && s[4].g[2] && s[2].g[4+x1^1]);
    if(ans) puts("Y");
    else puts("N"); 
}
void solve()
{
    char ch[10];
    int x1,y1,x2,y2;
    while(~scanf("%s",ch) && ch[0]!='E')
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);x1--,x2--; 
        if(ch[0]=='O') insert(1,1,n,x1,y1,x2,y2,1);
        if(ch[0]=='C') insert(1,1,n,x1,y1,x2,y2,0);
        if(ch[0]=='A') ask(x1,y1,x2,y2);
    }
}
int main()
{
    init();
    build(1,1,n);
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值