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

8 篇文章 0 订阅
8 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图求从 $(1,1)$ 到 $(n,n)$ 的所有路径,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值