BZOJ 1018 SHOI2008 堵塞的交通traffic 线段树

题目大意:给定一张2*n的网格图,多次改变某条边是否可用,多次查询某两个点是否联通

多(yi)年前的我看到这题的第一反应是:这题尼玛能做?

两个点之间的路径可能是这样的:

也可能是这样的:

甚至可能是这样的:

这题能写?

这题其实好写爆了

我们首先忽略第三种情况,假设所有对答案有贡献的边都在两个点的中间

那么我们以每一列为一个叶节点建立线段树

线段树的每个节点开一个二维数组a[2][2]

其中 a[x][y]记录当前区间的左端点的第x行和右端点的第y行是否联通

那么合并如下:

例如,a[0][0]有上图两种走法

其中左侧灰色框代表线段树的左区间,右侧灰色框代表线段树的右节点

其余三个同理

那么查询就直接去线段树上查询就行了。(暂且无视两边的点对答案的影响)

修改分两种:

如果修改的是一条竖着的边 那么就直接修改叶节点然后向上更新即可

预先开一个数组储存所有横着的边,如果修改的是一条横着的边那么直接修改这个数组的相应位置,然后找到被这条边分开的线段树节点,从该节点开始向上更新即可

那么这题就做完了。。。。等等

这样的情况如何处理?

其实很简单 从左边的点一直向左走 从右面的点一直向右走 走到两边最远的地方 这样对答案有贡献的所有边就都在这两个点中间了

这个用线段树就可以完成

大半夜写的乱七八糟一通乱改结果直接1A这你敢信?

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct abcd{
    bool a[2][2];
    abcd(bool _=false)
    {
        a[0][0]=a[1][1]=true;
        a[0][1]=a[1][0]=_;
    }
    bool* operator [] (int x)
    {
        return a[x];
    }
    friend abcd Merge(bool sta[2],abcd x,abcd y)
    {
        abcd re;
        re[0][0]=(x[0][0]&sta[0]&y[0][0])|(x[0][1]&sta[1]&y[1][0]);
        re[1][1]=(x[1][1]&sta[1]&y[1][1])|(x[1][0]&sta[0]&y[0][1]);
        re[0][1]=(x[0][0]&sta[0]&y[0][1])|(x[0][1]&sta[1]&y[1][1]);
        re[1][0]=(x[1][1]&sta[1]&y[1][0])|(x[1][0]&sta[0]&y[0][0]);
        return re;
    }
};
int n;
bool a[M][2];
struct Segtree{
    Segtree *ls,*rs;
    abcd status;
    Segtree():ls(0x0),rs(0x0) {}
    #define Push_Up(); status=Merge(a[mid],ls->status,rs->status);
    void Build_Tree(int x,int y)
    {
        int mid=x+y>>1;
        if(x==y) return ;
        (ls=new Segtree)->Build_Tree(x,mid);
        (rs=new Segtree)->Build_Tree(mid+1,y);
        Push_Up();
    }
    void Modify(int x,int y,int pos,int flag)
    {
        int mid=x+y>>1;
        if(x==y)
        {
            new (&status)abcd(flag);
            return ;
        }
        if(pos<=mid)
            ls->Modify(x,mid,pos,flag);
        else
            rs->Modify(mid+1,y,pos,flag);
        Push_Up();
    }
    void Refresh(int x,int y,int pos)
    {
        int mid=x+y>>1;
        if(pos==mid)
        {
            Push_Up();
            return ;
        }
        if(pos<mid)
            ls->Refresh(x,mid,pos);
        else
            rs->Refresh(mid+1,y,pos);
        Push_Up();
    }
    void _Get_Left(int x,int y,int &pos,abcd &sta,bool flag)
    {
        int mid=x+y>>1;
        if(x==y) return ;
        abcd temp=Merge(a[y],rs->status,sta);
        if( temp[0][flag] || temp[1][flag] )
            pos=mid+1,sta=temp,ls->_Get_Left(x,mid,pos,sta,flag);
        else
            rs->_Get_Left(mid+1,y,pos,sta,flag);
    }
    void Get_Left(int x,int y,int &pos,abcd &sta,bool flag)
    {
        int mid=x+y>>1;
        if(x==y) return ;
        if(pos<=mid)
            ls->Get_Left(x,mid,pos,sta,flag);
        else
        {
            rs->Get_Left(mid+1,y,pos,sta,flag);
            if(pos!=mid+1) return ;
            abcd temp=Merge(a[mid],ls->status,sta);
            if( temp[0][flag] || temp[1][flag] )
                pos=x,sta=temp;
            else
                ls->_Get_Left(x,mid,pos,sta,flag);
        }
    }
    void _Get_Right(int x,int y,int &pos,abcd &sta,bool flag)
    {
        int mid=x+y>>1;
        if(x==y) return ;
        abcd temp=Merge(a[x-1],sta,ls->status);
        if( temp[flag][0] || temp[flag][1] )
            pos=mid,sta=temp,rs->_Get_Right(mid+1,y,pos,sta,flag);
        else
            ls->_Get_Right(x,mid,pos,sta,flag);
    }
    void Get_Right(int x,int y,int &pos,abcd &sta,bool flag)
    {
        int mid=x+y>>1;
        if(x==y) return ;
        if(pos>mid)
            rs->Get_Right(mid+1,y,pos,sta,flag);
        else
        {
            ls->Get_Right(x,mid,pos,sta,flag);
            if(pos!=mid) return ;
            abcd temp=Merge(a[mid],sta,rs->status);
            if( temp[flag][0] || temp[flag][1] )
                pos=y,sta=temp;
            else
                rs->_Get_Right(mid+1,y,pos,sta,flag);
        }
    }
    abcd Get_Ans(int x,int y,int l,int r)
    {
        int mid=x+y>>1;
        if(x==l&&y==r)
            return status;
        if(r<=mid)
            return ls->Get_Ans(x,mid,l,r);
        if(l>mid)
            return rs->Get_Ans(mid+1,y,l,r);
        return Merge(a[mid],ls->Get_Ans(x,mid,l,mid),rs->Get_Ans(mid+1,y,mid+1,r));
    }
}*tree=new Segtree;
void Modify(int x1,int y1,int x2,int y2,bool flag)
{
    if(x1==x2)
    {
        if(y1>y2) swap(y1,y2);
        a[y1][x1-1]=flag;
        tree->Refresh(1,n,y1);
        return ;
    }
    tree->Modify(1,n,y1,flag);
}
void Query(int x1,int y1,int x2,int y2)
{
    if(y1>y2) swap(x1,x2),swap(y1,y2);

    abcd temp(false);
    tree->Get_Left(1,n,y1,temp,x1-1);
    x1=temp[0][x1-1]?1:2;

    new (&temp)abcd(false);
    tree->Get_Right(1,n,y2,temp,x2-1);
    x2=temp[x2-1][0]?1:2;

    temp=tree->Get_Ans(1,n,y1,y2);
    puts(temp[x1-1][x2-1]?"Y":"N");
}
int main()
{
    int x1,y1,x2,y2;
    char p[10];
    cin>>n;
    tree->Build_Tree(1,n);
    while(1)
    {
        scanf("%s",p);
        if(p[0]=='C')
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2),Modify(x1,y1,x2,y2,false);
        if(p[0]=='O')
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2),Modify(x1,y1,x2,y2,true);
        if(p[0]=='A')
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2),Query(x1,y1,x2,y2);
        if(p[0]=='E')
            break;
    }
    return 0;
}
时间: 2024-10-12 04:34:28

BZOJ 1018 SHOI2008 堵塞的交通traffic 线段树的相关文章

BZOJ 1018: [SHOI2008]堵塞的交通traffic [线段树 区间信息]

1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 3064  Solved: 1027[Submit][Status][Discuss] Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路. 小人国的交通

数据结构(线段树):BZOJ 1018: [SHOI2008]堵塞的交通traffic

1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 2638  Solved: 864 Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路. 小人国的交通状况非常槽糕.有的时候由于交通堵塞,两座城市之间的道

【BZOJ1018】[SHOI2008]堵塞的交通traffic 线段树

[BZOJ1018][SHOI2008]堵塞的交通traffic Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路. 小人国的交通状况非常槽糕.有的时候由于交通堵塞,两座城市之间的道路会变得不连通,直到拥堵解决,道路才会恢复畅通.初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度发达的

[BZOJ 1018][SHOI2008]堵塞的交通traffic(线段树)

Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可 以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个 城市和3C-2条道路. 小人国的交通状况非常槽糕.有的时候由于交通堵塞,两座城市之间的道路会变得不连通, 直到拥堵解决,道路才会恢复畅通.初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度 发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入

[BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】

题目链接:BZOJ - 1018 题目分析 这道题就说明了刷题少,比赛就容易跪..SDOI Round1 Day2 T3 就是与这道题类似的..然而我并没有做过这道题.. 这道题是线段树维护联通性的经典模型. 我们线段树的一个节点表示一个区间的联通性,有 6 个 bool 值,表示这个区间的 4 个角上的点之间的联通性. 然后用两个子区间的联通性和两个子区间之间的连边情况合并出整个区间的联通性. 修改某条边时,先在边的数组中修改,然后从这条边所在的点的线段树叶子开始向上 Update . 询问两

bzoj 1018: [SHOI2008]堵塞的交通traffic

由于只有两行,对一个区间可以维护四个角上的连通性 一共6个量,满足区间可加性,用线段树维护,查询时找出3段的量,看是否满足题解上的三种情况之一. 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<queue> 7 #include<algorithm> 8

bzoj1018 [SHOI2008]堵塞的交通traffic——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1018 线段树竟然还可以这样用! 维护的是一个矩形内部的连通情况,四个顶点之间有6中连通方式: 因为连通情况具有可合并性,所以可以用线段树来维护! 这篇博客写得很好:https://www.cnblogs.com/DUXT/p/6029815.html 茅塞顿开之后看到这篇博客,觉得写得很好呢:https://www.cnblogs.com/MashiroSky/p/5973686.html

bzoj1018: [SHOI2008]堵塞的交通traffic 线段树

线段树维护每一块左上到左下.右上到右下.左上到右上.左下到右下.左上到右下.左下到右上的联通情况. #include<bits/stdc++.h> #define N 100005 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=1,int r=n,int k=1 u

1018: [SHOI2008]堵塞的交通traffic

1018: [SHOI2008]堵塞的交通traffic 链接 分析: 用线段树维护区间的四个端点的联通情况,然后查询的时候,把所有覆盖到的区间合并起来即可. 六种情况左上到右上(左边到右边的情况)……,左上到左下(同一侧相互到达的情况)…… 同一侧相互到达的情况,查询[l,r]是查的不完全.因为还有可能是先往左边走几步,下去,在走回来.这时候,查询一下[1,l]的情况,或起来即可. 代码: 1 #include<cstdio> 2 #include<algorithm> 3 #i