【bzoj1018】堵塞的交通

题意

一个\(2*n\)的格子,相邻格子之间有一条道路。初始时道路是不通的。

\(C\)个操作,每个操作为三种中的一种:(1)某条道路连起来;(2)某条道路断开;(3)询问某两个格子是否相连。

数据范围:\(C\leq 100000\)

分析

这的确是一道好题。

不仅体现在思想的巧妙,而且体现在其一题多解。

参阅了百度前两页的题解,有所感想。

思路1:线段树

前人之述备矣。

用线段树维护区间内的信息:

\(a1[2][2]\):a1[i][j]表示左端点的\(i\)到右端点的\(j\)是否联通;

\(a2[2]\):a2[i]表示左或者右端点的上下是否联通;

还需要用到中点的信息\(b[2]\):mid与mid+1是否联通,来完成合并。

注意是块内合并。

最后的询问才考虑到块外的情况。

时间复杂度:\(O(n\log n)\)

一份被我加了注释的代码 from invoid的题解

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 800000 + 10;

char s[20]; //操作
int x1,x2,y1,y2,c;

struct Segtree {
    #define lc(x) ((x)<<1)
    #define rc(x) (((x)<<1)|1)
    #define root 1

    struct Status {
        int a1[2][2],a2[2];
        //a1表示左右断电联通情况,a2表示左右端点分别上下联通情况
    }s[maxn];

    bool b[maxn][2];
    int l[maxn],r[maxn],m[maxn];    //l[x]:左端点  r[x]:右端点  m[x]:中点 

    Status update(Status s1,Status s2,bool b[]) {   //左态,右态,中间态
        Status res;
        for(int i=0;i<=1;i++)
        for(int j=0;j<=1;j++)
            res.a1[i][j]=s1.a1[i][0] && b[0] && s2.a1[0][j] || s1.a1[i][1] && b[1] && s2.a1[1][j];
        //基本联通,此时s1.a1已经包括了连出去的情况

        res.a2[0]=s1.a2[0] || s1.a1[0][0] && b[0] && s2.a2[0] && b[1] && s1.a1[1][1];
        res.a2[1]=s2.a2[1] || s2.a1[0][0] && b[0] && s1.a2[1] && b[1] && s2.a1[1][1];
        //弄清一个误区:s[i]指表示[l,r]中的情况,最后处理答案的时候才考虑连到外面的情况 

        return res;
    }

    Status access (int x,int y1,int y2) {
        if(y1<=l[x] && r[x]<=y2) return s[x];
        else if(y2<=m[x]) return access(lc(x),y1,y2);
        else if(y1>m[x]) return access(rc(x),y1,y2);
        else return update(access(lc(x),y1,y2),access(rc(x),y1,y2),b[x]);
        //access,b[x]为中间态
    }

    void change(bool k,int x,int x1,int y1,int x2,int y2) {
        if(x1==x2 && y1==m[x]) {    //同一行相邻两个
            b[x][x1]=k;             //x的mid到mid+1的连通性为k
            s[x]=update(s[lc(x)],s[rc(x)],b[x]);    //update
        }
        else if(l[x]==r[x])         //上下联通
            s[x].a1[0][1]=s[x].a1[1][0]=s[x].a2[0]=s[x].a2[1]=k;    //都是k
        else {
            change(k,y2<=m[x]?lc(x):rc(x),x1,y1,x2,y2); //递归
            s[x]=update(s[lc(x)],s[rc(x)],b[x]);    //update
        }
    }

    void ask(int x1,int y1,int x2,int y2) {
        Status left=access(root,1,y1),  //1到y1
               right=access(root,y2,c), //y2到c
               mid=access(root,y1,y2);  //y1到y2
        bool res=false;
        for(int i=0;i<=1;i++)
        for(int j=0;j<=1;j++)
            if(mid.a1[i][j]&&(i==x1||left.a2[1])&&(j==x2||right.a2[0])) {
                //若mid[i][j]联通,i=x1或左联通,j=x2或右联通,则res=1
                res=true; break;
            }
        printf(res?"Y\n":"N\n");    //返回res
    }

    void build(int x,int y1,int y2) {
        l[x]=y1,r[x]=y2,m[x]=(l[x]+r[x])>>1;
        if(y1==y2)
            s[x].a1[0][0]=s[x].a1[1][1]=true;
        else {
            build(lc(x),y1,m[x]);
            build(rc(x),m[x]+1,y2);
        }
    }
    //连通
}seg;

int main() {
    scanf("%d",&c);
    seg.build(1,1,c);
    while(1) {
        scanf("%s",s);
        if(s[0]=='E') break;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        --x1; --x2; //从0开始

        if(y1>y2) {
            swap(x1,x2);
            swap(y1,y2);
        }
        //y1在前,y2在后 

        if(s[0]=='O') seg.change(1,1,x1,y1,x2,y2);  //增边
        else if(s[0]=='C') seg.change(0,1,x1,y1,x2,y2); //删边
        else seg.ask(x1,y1,x2,y2);  //查连
    }
    return 0;
}

思路2:分块+并查集

http://blog.csdn.net/cjk_cjk/article/details/43489473

并查集有一定的局限性:只支持快速合并,不支持快速删除。

所以我们打算直接合并+初始化,而不使用删除。

但是初始化的速度较慢,所以我们考虑分块,对于询问只需要合并各个块即可。

根据平衡规划的思想,我们要是的初始化的速度尽可能快,而且块间的合并尽可能快,所以我们取\(O(\sqrt n)\)分一块。

Open:在同一块中就维护并查集,在不同块互相连通就用一个link数组记录

Close:在同一块中就把块中元素的fa[]全部重新计算,在不同块中直接把link赋零

Ask:判断A及能到达的4个顶点,再递归4个顶点到其他块的顶点,看顶点与B是否连通

时间复杂度:\(O(n\sqrt n)\)

思路3:定期重建 + [按秩合并 & 路径压缩]并查集

离线处理。

并查集有一定的局限性:只支持快速合并,不支持快速删除,但是按秩合并的并查集可以逐个删除。

所以我们打算直接合并+顺序撤除。

按照操作顺序,每\(\sqrt c\)个就分一次块。

每次处理出块前的并查集,然后按秩合并处理块内的询问,然后每次再撤销回块的初始状态。

时间复杂度:\(O(n\sqrt n\log n)\)

思路4:线段树+并查集

http://www.cnblogs.com/ccz181078/p/5346077.html

离线处理。

将每条存在的边的按存在时间插入到线段树中,对线段dfs一次,用按秩合并、支持撤销的并查集维护连通性并回答询问。

此方法适用于离线的图连通性维护。

真的是一种很优美的想法啊。

时间复杂度:\(O(n\log^2 n)\)

小结

(1)线段树的一些理解

①线段树的本质在于维护区间内的信息,假如要维护区间外的信息,正如开始我的想法,或许能够实现,但是未必好些,而且通常也可以转化为区间内信息的维护。

②线段树通常能够维护区间的连通性

(2)并查集的一些理解

并查集能支持快速合并,快速查询,前提:一个条件就可以导致一次合并。

对于删除,通常可以做这样的优化:

①转化为初始化+合并,通过平衡规划(按位置分块等)优化

②转化为按顺序合并,按顺序删除,这样可以通过按秩合并来实现,通过平衡规划(按操作顺序分块)或者回溯(本题线段树回溯)优化

(3)离线处理的一些方法总结

可以使用离线的前提:多个操作

特殊化:多个询问

下面列举一些方法,并举一些例子。

①回代法

bzoj1015

②按照操作顺序分块,定期重建

本题bzoj1018

③莫队算法

小Z的袜子

④确定左端点,移动右端点

HH的项链

⑤单调性,two pointer

noip2016Day1T2:树剖+树状数组

⑥cdq分治

bzoj简单题

⑦快速覆盖区间,从前往后遍历

本题bzoj1018,用线段树按照操作时间插入边,然后遍历线段树,访问每个时间(叶子节点)

能用到离线处理的远远不止这些。

只是积累一点经验罢了。

毕竟我不是一些可能存在的大神。

说什么劳资纯刷题就可以虐爆你。。

【bzoj1018】堵塞的交通的更多相关文章

  1. [BZOJ1018]堵塞的交通traffic

    Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一 ...

  2. BZOJ1018 堵塞的交通(线段树)

    题目很好明白,然后实现很神奇.首先如果考虑并查集的话,对于删边和加边操作我们无法同时进行.然后暴力分块的话,复杂度是O(n sqrt n) ,不是很优.于是看了题解,发现了线段树的神奇用途. 我们维护 ...

  3. 【BZOJ1018】堵塞的交通(线段树)

    [BZOJ1018]堵塞的交通(线段树) 题面 Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可 以被看成是一个2行C列的矩形网 ...

  4. 【BZOJ1018】[SHOI2008]堵塞的交通

    [BZOJ1018][SHOI2008]堵塞的交通 题面 bzoj 洛谷 洛谷 题解 菊队讲要用线段树维护连通性,但是好像没人写 解法一 将所有的加边删边离线,然后以最近删除时间为边权,$LCT$维护 ...

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

    [BZOJ1018][SHOI2008]堵塞的交通traffic Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个 ...

  6. [bzoj1018][SHOI2008]堵塞的交通traffic_线段树

    bzoj-1018 SHOI-2008 堵塞的交通traffic 参考博客:https://www.cnblogs.com/MashiroSky/p/5973686.html 题目大意:有一天,由于某 ...

  7. bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

    http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 ...

  8. 【bzoj1018】[SHOI2008]堵塞的交通traffic

    1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 2887  Solved: 954[Submit ...

  9. [BZOJ1018][SHOI2008]堵塞的交通traffic 线段树维护连通性

    1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MB Submit: 3795  Solved: 1253 [Sub ...

随机推荐

  1. SqlSever基础 top 后面的n 由一个简单的计算式子计算得出

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  2. MSM8909平台 LED背光的控制

    之前齐师兄问我,是不是应该有一个文件记录背光灯的亮度,我说理论上有,但是在哪里我真的还没有见过.只知道在调LCD驱动的时候会调用一个背光控制的函数,传进来一个亮度值就可以配置亮度了,至于这个函数是谁调 ...

  3. (转)Genymotion安装virtual device的“unable to create virtual device, Server returned Http status code 0”的解决方法

    网络原因无法下载virtual device,status 为0表示服务器没有响应.FQ下载吧,有VPN的小伙伴推荐这种. 或者直接手动下载ova虚拟机文件,然后将虚拟机文件导入到virtualbox ...

  4. 部署PDA程序的时候存储不足的解决办法

    通常Windows Mobile的存储空间分为程序内存和存储内存,默认都比较小,当程序比较大的时候可能无法正常部署到设备上面,针对此问题可采用如下办法解决:1.通过VS修改,工具--选项--设备--选 ...

  5. C# 十进制与十六进制互转

    1.从十六进制转换为十进制 /// <summary> /// 十六进制转换到十进制 /// </summary> /// <param name="hex&q ...

  6. Jenkins插件及 测试源码

    Jenkins 插件: https://updates.jenkins-ci.org/download/plugins/ 小米的一份android源码,测试工具,用于抢红包: https://gith ...

  7. YII的关联查询

    先看数据表中的关系是怎样的: customer表中的关系如下: order中的表为: 先在customer中获得order的数据,并与之关联,在 helloController.php 中,代码如下 ...

  8. Linux配置apache等系列

    1.Linux下安装.配置PHP环境 2.ubuntu12.0.4安装apache, php ,mysql 3   CentOs中mysql的安装与配置

  9. 一切都是对象 Thinking in Java 第二章

    2.1 用引用操作对象 1.对象和引用联系起来,就可以通过引用来操作对象: 2.引用亦可单独存在,即没有和任何对象联系起来:对没有和对象联系起来的引用操作,会报错: 2.2 必须由你创建所有对象 1. ...

  10. Connection的使用

    package com.cz.test.util.collection; import java.util.ArrayList;import java.util.Collection;import j ...