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

38 篇文章 0 订阅

题目大意:给定一张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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值