• 【洛谷P5787】二分图 /【模板】线段树分治


    题目

    题目链接:https://www.luogu.com.cn/problem/P5787
    神犇有一个 (n) 个节点的图。
    因为神犇是神犇,所以在 (k) 时间内有 (m) 条边会出现后消失。
    神犇要求出每一时间段内这个图是否是二分图。
    这么简单的问题神犇当然会做了,于是他想考考你。
    (n,k=10^5,m=2 imes 10^5)

    思路

    建立一棵线段树,把每一个操作的区间 ([l,r]) 在线段树上找到,并在每一个区间的 vector 中加入连接 (x,y) 两点的操作。
    然后深度优先搜索整棵线段树,搜索到一个区间 ([l,r]) 时,把这个区间的所有操作执行,具体的,为了判断是不是二分图,我们可以采用扩展域并查集,把每一个点拆成两个点,然后类比黑白染色,黑点连白点,白点连黑点。如果某个时刻存在一个点的黑点和白点在同一集合内,说明存在奇环,([l,r]) 均不是二分图。
    否则继续分治,如果到达叶子时依然没有奇环,那么就是二分图。
    回溯的时候需要撤销并查集,考虑采用不路径压缩,只按秩合并的并查集即可。
    估计只有我一个人按秩合并一直以为是按照大小吧。其实是按照深度。
    时间复杂度 (O(klog klog m))

    代码

    #include <bits/stdc++.h>
    #define mp make_pair
    #define ST first
    #define ND second
    using namespace std;
    
    const int N=200010;
    int n,m,t,father[N*2],dep[N*2];
    stack<int> st;
    
    int find(int x)
    {
    	return x==father[x]?x:find(father[x]);
    }
    
    bool merge(int x,int y)
    {
    	x=find(x); y=find(y);
    	if (x==y) return 1;
    	if (dep[x]<dep[y]) swap(x,y);
    	father[y]=x; dep[x]+=(dep[x]==dep[y]);
    	st.push(y);
    	if (find(x)==find(x+n) || find(y)==find(y+n)) return 0;
    	return 1;
    }
    
    struct SegTree
    { 
    	vector<pair<int,int> > e[N*4];
    	
    	void update(int x,int l,int r,int ql,int qr,int p,int q)
    	{
    		if (ql<=l && qr>=r)
    		{
    			e[x].push_back(mp(p,q));
    			return;
    		}
    		int mid=(l+r)>>1;
    		if (ql<=mid) update(x*2,l,mid,ql,qr,p,q);
    		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,p,q);
    	}
    	
    	void solve(int x,int l,int r)
    	{
    		int cnt=st.size(); bool flag=1;
    		for (int i=0;i<e[x].size();i++)
    		{
    			int p=e[x][i].ST,q=e[x][i].ND;
    			if (!merge(p,q+n)) { flag=0; break; }
    			if (!merge(p+n,q)) { flag=0; break; }
    		}
    		if (!flag)
    			for (int i=l;i<=r;i++) printf("No
    ");
    		else if (l<r)
    		{
    			int mid=(l+r)>>1;
    			solve(x*2,l,mid); solve(x*2+1,mid+1,r);
    		}
    		else printf("Yes
    ");
    		for (;st.size()>cnt;st.pop())
    		{
    			int x=st.top();
    			dep[father[x]]-=(dep[father[x]]==dep[x]); father[x]=x;
    		}
    	}
    }seg;
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&t);
    	for (int i=1,x,y,l,r;i<=m;i++)
    	{
    		scanf("%d%d%d%d",&x,&y,&l,&r);
    		seg.update(1,1,t,l+1,r,x,y);
    	}
    	for (int i=1;i<=n*2;i++)
    		father[i]=i;
    	seg.solve(1,1,n);
    	return 0;
    }
    
  • 相关阅读:
    jumpserver安装教程
    正则表达式基础->
    Awk基础
    shell脚本练习题->1
    idea开发工具下载安装教程
    shell 数组基础->
    动荡的国庆前后
    Linux命令之查找
    2013年9月游戏测试总结-文档习惯
    将C#程序做成服务后服务自动停止的问题
  • 原文地址:https://www.cnblogs.com/stoorz/p/14402511.html
Copyright © 2020-2023  润新知