zoukankan      html  css  js  c++  java
  • P5787 线段树分治

    https://www.luogu.com.cn/problem/P5787
    同BZOJ4025

    (n) 个点的图,(m) 条边分别在各自的 ([l,r]) 时间段内会出现,对于 ([1,k]) 内每个时间点求这个图是否是二分图

    用到了线段树分治,就是以时间为下标建立一个线段树,然后根据线段树的区间操作,每个时间段 ([l,r]) 可以分成 (O(log k)) 个区间(时间区间),然后对这些区间做上标记,最后 dfs 线段树,线段树上的根节点对应的就是一个时间点,想判断这个时间点是不是二分图只要把它所有父亲的标记都考虑上就行了
    也就是每进入一个线段树节点,就把这个节点上记录的所有边都加入图,如果出现奇环(二分图等价于无奇环),这个区间内所有时间点都是不是,否则就向下继续 dfs,离开节点是再把这些边删掉

    要维护奇环,还得用带撤销的并查集,把每个图中的点拆成两个点((i)(i+n) 来表示),如果加入边 ((u,v))(u,v) 在同一集合那么就不是二分图,否则就在并查集中合并 ((u,v+n),(v,u+n)) 所在的集合
    可以理解为二分图就是要求把点分成两类,而 (i,i+n) 分别代表 (i) 属于第一类还是第二类。把要成立必须同时成立的并查集中的点放入一个集合
    边必须在不同类的两个点之间,所以有了如上的连边(并查集中连)规则;同理,如果 (u,v) 已经在同一集合,它们必须同时成立,也就是同时属于一类,那么出现矛盾

    为了支持撤销,其实不用可持久化什么的,只要来一个栈维护每次更改并查集数组的信息就行了,然后一步一步回退
    不过因此也就不能用路径压缩了,需要按大小合并

    我居然把并查集写错导致WA了好久

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 200006
    int n,m,k;
    struct Edge{
    	int u,v;
    };
    struct Node{
    	Node *ls,*rs;
    	std::vector<Edge>edge;
    }dizhi[N*2],*root=&dizhi[0];
    int tot;
    void build(Node *tree,int l,int r){
    	if(l==r) return;
    	tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
    	int mid=(l+r)>>1;
    	build(tree->ls,l,mid);build(tree->rs,mid+1,r);
    }
    void insert(Node *tree,int l,int r,int ql,int qr,Edge e){
    	if(ql<=l and r<=qr) return tree->edge.push_back(e),void();
    	int mid=(l+r)>>1;
    	if(ql<=mid) insert(tree->ls,l,mid,ql,qr,e);
    	if(qr>mid) insert(tree->rs,mid+1,r,ql,qr,e);
    }
    int stack[N*4],top;
    int size[N*2],fa[N*2];
    inline int find(int k){return k==fa[k]?k:find(fa[k]);}
    inline void merge(int x,int y){
    	if(x==y) return;
    	if(size[x]>size[y]) x^=y,y^=x,x^=y;
    	stack[++top]=x;stack[++top]=size[y];
    	fa[x]=y;size[y]+=size[x];
    }
    void work(Node *tree,int l,int r){
    	int edge_size=tree->edge.size(),last_top=top;
    	for(reg int i=0,u,v,rootu,rootv;i<edge_size;i++){
    		u=tree->edge[i].u;v=tree->edge[i].v;
    		rootu=find(u);rootv=find(v);
    		if(rootu==rootv){
    			for(reg int j=l;j<=r;j++) puts("No");
    			goto NO;
    		}
    		merge(find(u+n),rootv);merge(find(v+n),rootu);
    	}
    	if(l==r) puts("Yes");
    	else{
    		int mid=(l+r)>>1;
    		work(tree->ls,l,mid);work(tree->rs,mid+1,r);
    	}
    	NO:;
    	while(top>last_top){
    		size[fa[stack[top-1]]]=stack[top];
    		fa[stack[top-1]]=stack[top-1];
    		top-=2;
    	}
    }
    int main(){
    	n=read();m=read();k=read();
    	build(root,1,k);
    	for(reg int i=1;i<=n;i++) fa[i]=i,fa[i+n]=i+n,size[i]=size[i+n]=1;
    	reg int u,v,l,r;
    	while(m--){
    		u=read();v=read();l=read();r=read();
    		if(l^r) insert(root,1,k,l+1,r,(Edge){u,v});
    	}
    	work(root,1,k);
    	return 0;
    }
    
  • 相关阅读:
    怎样重定向404页面?404页面重定向 PHP如何使404页面重定向
    css选择器(1)——元素选择器、类名和id选择器
    style对象的cssText方法
    css选择器(2)——属性选择器和基于元素结构关系的选择器
    js的闭包中关于执行环境和作用链的理解
    attachEvent和addEventListener
    使用js获取页面的各种高度
    课堂知识
    分析一个文本文件(英文文章)中各个词出现的频率,并且把频率最高的10个词打印出来。
    每日心情
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13866997.html
Copyright © 2011-2022 走看看