【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. ListableBeanFactory

    ListableBeanFactory public interface ListableBeanFactory extends BeanFactory 该接口中定义了可以获取配置中所有bean的信息 ...

  2. MFC之简单计算器

    1.界面 2.变量 combobox的变量类型是CComBoBox类型,三个输入框是double类型: 它的type是Drop List 3.代码 (1).初始化combobox BOOL Ccalc ...

  3. HDU4686 Arc of Dream 矩阵快速幂

    Arc of Dream Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Tota ...

  4. CodeForces 152C Pocket Book

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Prac ...

  5. 【Unity3D游戏开发】Application.systemLanguage无法区分简体中文和繁体中文 (二六)

    游戏发布,语言本地化需要繁体中文和简体中文 iOS8版本之前没问题,iOS9上无法正常识别这两种语言 原因是在iOS9上,Unity通过Application.systemLanguage返回的简体中 ...

  6. Create Hierarchical Tree To Control Records In Oracle Forms

    Download Source Code Providing an example form for creating hierarchical trees in Oracle Forms to co ...

  7. wince下的CPU和内存占用率计算

    #include <Windows.h> DWORD Caculation_CPU(LPVOID lpVoid) { MEMORYSTATUS MemoryInfo; DWORD Perc ...

  8. [SAP ABAP开发技术总结]面向对象OO

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. HDU 5724 Chess(国际象棋)

    HDU 5724 Chess(国际象棋) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  10. Chrome浏览器的密码隐患

    谷歌浏览器的密码填充使得登陆账号很方便 但在你了解了Chrome的密码特性机制后,你该做点什么了 1.如何查看已保存的密码 Chrome 密码管理器的进入方式:右侧扳手图标→设置→显示高级设置→密码和 ...