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


    线段树分治真的难写。

    二分图判定的条件是图中不存在奇环。我们可以用带权并查集来维护。现在的难点就在于对线段树(DFS)时回溯的时候如何删除影响。

    这里可以使用可删并查集:注意到我们加入影响和删除影响的过程就是维护栈的过程,我们可以放弃并查集的路径压缩(我们按秩合并也可以保证复杂度),并在加入边时记录操作,并在删除时改回去。

    • 这题的一些细节:
    1. 当你发现加入一条边后已经不符合二分图的限制了,则你此后(到回溯前)的所有边都可以不用加了(显然)。
    2. 按秩合并就是你给每个块顺便记录一个大小,合并的时候把小的往大的里加,即可保证复杂度(类似于重链剖分的复杂度分析)。

    啊啊啊说白了就是烦。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    
    using namespace std;
    
    const int N=200009;
    struct Node
    {
    	int first,second,ok,A,B;
    };
    int n,m,K,f[N],fuck[N],flag,flagk,flagp,siz[N];
    vector <Node> b[N*4];
    
    void Modify(int k,int l,int r,int x,int y,int p,int q)
    {
    	if(l>=x&&r<=y)
    	{
    		b[k].push_back((Node){p,q,0,0,0});
    		return;
    	}
    	int mid=l+r>>1;
    	if(mid>=x)
    		Modify(k<<1,l,mid,x,y,p,q);
    	if(mid<y)
    		Modify(k<<1|1,mid+1,r,x,y,p,q);
    }
    
    int find(int x)
    {
    	if(x==f[x]) return x;
    	return find(f[x]);
    }
    
    int Get_Dis(int x)
    {
    	if(x==f[x]) return 0;
    	return fuck[x]^Get_Dis(f[x]);
    }
    
    void init()
    {
    	scanf("%d %d %d",&n,&m,&K);
    	for (int i=1;i<=m;i++)
    	{
    		int x,y,s,t;
    		scanf("%d %d %d %d",&x,&y,&s,&t);
    		if(s==t) continue;
    		Modify(1,1,K,s+1,t,x,y);
    	}
    	for (int i=1;i<=n;i++)
    		f[i]=i,siz[i]=1;
    }
    
    void dfs(int k,int l,int r)
    {
    	if(!flag)
    		for (int i=0;i<b[k].size();i++)
    		{
    			Node v=b[k][i];
    			int x=v.first,y=v.second;
    			int A=find(x),B=find(y);
    			if(siz[A]<siz[B])
    				swap(x,y),swap(A,B);
    			if(A!=B)
    			{
    				siz[A]+=siz[B];
    				f[B]=A,fuck[B]=Get_Dis(y)^Get_Dis(x)^1;
    				b[k][i].ok=1,b[k][i].A=A,b[k][i].B=B;
    			}
    			else if(!(Get_Dis(x)^Get_Dis(y)))
    			{
    				flag=1;
    				flagk=k;
    				flagp=i;
    				break;
    			}
    		}
    	if(l==r)
    		puts(flag?"No":"Yes");
    	else
    	{
    		int mid=l+r>>1;
    		dfs(k<<1,l,mid);
    		dfs(k<<1|1,mid+1,r);
    	}
    	if(!flag)
    	{
    		for (int i=b[k].size()-1;i>=0;i--)
    		{
    			Node v=b[k][i];
    			if(!v.ok)
    				continue;
    			f[v.B]=v.B,fuck[v.B]=0,siz[v.A]-=siz[v.B];
    		}
    	}
    	else if(k==flagk)
    	{
    		flag=0;
    		if(flagp==0) return;
    		for (int i=flagp-1;i>=0;i--)
    		{
    			Node v=b[k][i];
    			if(!v.ok)
    				continue;
    			f[v.B]=v.B,fuck[v.B]=0,siz[v.A]-=siz[v.B];
    		}
    	}
    }
    
    int main()
    {
    	init();
    	dfs(1,1,K);
    	return 0;
    }
    
    由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
  • 相关阅读:
    2012工作计划!
    造船篇系统实施(新老衔接工作)
    Android学习摘要一之Android历史
    DEVReport控件使用方法
    造船篇 前传
    软件实施(1)
    茫然疑问未来
    造船篇钢材管理
    wcf身份验证问题
    IDL中关于波段计算的问题
  • 原文地址:https://www.cnblogs.com/With-penguin/p/13082494.html
Copyright © 2020-2023  润新知