zoukankan      html  css  js  c++  java
  • 二分图 /【模板】线段树分治

    XVI.二分图 /【模板】线段树分治

    本题有两种做法。一是所谓的“正解”线段树分治,复杂度\(O(n\log n\log k)\)。思路比较简单,敲起来也简单,就是复杂度不太优秀。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    int n,m,lim,cnt,dsu[200100],sz[200100];
    int find(int x){
    	return dsu[x]==x?x:find(dsu[x]);
    }
    struct opt{
    	int u,v,su,sv;
    	opt(int a=0,int b=0,int c=0,int d=0){u=a,v=b,su=c,sv=d;}
    };
    stack<opt>s;
    int merge(int x,int y){
    	x=find(x),y=find(y);
    	if(x==y)return 0;
    	s.push(opt(x,y,sz[x],sz[y]));
    	if(sz[x]<sz[y])dsu[x]=y,sz[y]+=sz[x];
    	else dsu[y]=x,sz[x]+=sz[y];
    	return 1;
    }
    void Pop(){
    	dsu[s.top().u]=s.top().u,dsu[s.top().v]=s.top().v,sz[s.top().u]=s.top().su,sz[s.top().v]=s.top().sv,s.pop();
    }
    bool link(int x,int y){
    	int tot=merge(x+n,y)+merge(x,y+n);
    	if(find(x)!=find(x+n)&&find(y)!=find(y+n))return true;
    	while(tot--)Pop();
    	return false;
    }
    vector<pair<int,int> >v[400100];
    void modify(int x,int l,int r,int L,int R,int f,int g){
    	if(l>R||r<L)return;
    	if(L<=l&&r<=R){v[x].push_back(make_pair(f,g));return;}
    	modify(lson,l,mid,L,R,f,g),modify(rson,mid+1,r,L,R,f,g);
    }
    void dfs(int x,int l,int r){
    	int res=0,len=s.size();
    	for(int i=0,tmp;i<v[x].size();i++){
    		tmp=link(v[x][i].first,v[x][i].second);
    		res+=!tmp;
    	}
    	cnt+=res;
    	if(l==r){puts(cnt?"No":"Yes"),cnt-=res;return;}
    	dfs(lson,l,mid),dfs(rson,mid+1,r);
    	while(s.size()>len)Pop();
    	cnt-=res;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&lim);
    	for(int i=1;i<=2*n;i++)dsu[i]=i,sz[i]=1;
    	for(int i=1,x,y,l,r;i<=m;i++){
    		scanf("%d%d%d%d",&x,&y,&l,&r),l++;
    		if(l<=r)modify(1,1,lim,l,r,x,y);
    	}
    	dfs(1,1,lim);
    	return 0;
    }
    

    二是所谓的“暴力解法”,但是复杂度反而更优,为\(O(n\log n)\)的LCT。

    具体想法是,我们维护原图关于删除时间的最大生成森林。

    当加入一条新边构成环时:

    我们在LCT中提取出来新边的路径。如果这条新边构成奇环(即这条路径的size为奇)的话,则直到这个奇环的删除时间最小的那条边被删除为止,这张图都不会是二分图。

    正因如此,我们才会贪心地维护最大生成树,因为最大生成树就意味着构成的奇环是正确的,保证直到环上第一条边删去前,它都不会是二分图。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,lim,res[100100];
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    struct LCT{
    	int fa,ch[2],mn,val,sz;
    	bool rev;
    }t[300100];
    inline int identify(int x){
    	if(x==t[t[x].fa].ch[0])return 0;
    	if(x==t[t[x].fa].ch[1])return 1;
    	return -1;
    }
    inline void pushup(int x){
    	t[x].mn=x;
    	t[x].sz=(x<=n);
    	if(lson){
    		if(t[t[x].mn].val>t[t[lson].mn].val)t[x].mn=t[lson].mn;
    		t[x].sz+=t[lson].sz;
    	}
    	if(rson){
    		if(t[t[x].mn].val>t[t[rson].mn].val)t[x].mn=t[rson].mn;
    		t[x].sz+=t[rson].sz;
    	}
    }
    inline void REV(int x){
    	t[x].rev^=1,swap(lson,rson);
    }
    inline void pushdown(int x){
    	if(!t[x].rev)return;
    	if(lson)REV(lson);
    	if(rson)REV(rson);
    	t[x].rev=0;
    }
    inline void rotate(int x){
    	register int y=t[x].fa;
    	register int z=t[y].fa;
    	register int dirx=identify(x);
    	register int diry=identify(y);
    	register int b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[y].fa=x,t[x].ch[!dirx]=y;
    	pushup(y),pushup(x);
    }
    inline void pushall(int x){
    	if(identify(x)!=-1)pushall(t[x].fa);
    	pushdown(x);
    }
    inline void splay(int x){
    	pushall(x);
    	while(identify(x)!=-1){
    		register int fa=t[x].fa;
    		if(identify(fa)==-1)rotate(x);
    		else if(identify(x)==identify(fa))rotate(fa),rotate(x);
    		else rotate(x),rotate(x);
    	}
    }
    inline void access(int x){
    	for(register int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);
    }
    inline void makeroot(int x){
    	access(x),splay(x),REV(x);
    }
    inline int findroot(int x){
    	access(x),splay(x);
    	pushdown(x);
    	while(lson)x=lson,pushdown(x);
    	splay(x);
    	return x;
    }
    inline void split(int x,int y){
    	makeroot(x),access(y),splay(y);
    }
    inline void link(int x,int y){
    	makeroot(x),t[x].fa=y;
    }
    inline void cut(int x,int y){
    	split(x,y),t[y].ch[0]=t[x].fa=0,pushup(y);
    }
    pair<int,int>p[200100];
    vector<int>stt[100100],fin[100100];
    bool on[200100];
    int main(){
    	scanf("%d%d%d",&n,&m,&lim);
    	for(int i=1;i<=n+m;i++)t[i].mn=i;
    	for(int i=1;i<=n;i++)t[i].val=0x3f3f3f3f,t[i].sz=1;
    	for(int i=1,a,b,c,d;i<=m;i++){
    		scanf("%d%d%d%d",&a,&b,&c,&d),stt[c].push_back(i),fin[d].push_back(i);
    		t[i+n].val=d;
    		p[i]=make_pair(a,b);
    	}
    	for(int i=0;i<lim;i++){
    		for(int j=0;j<stt[i].size();j++){
    			int id=stt[i][j];
    			if(findroot(p[id].first)!=findroot(p[id].second))link(p[id].first,id+n),link(p[id].second,id+n),on[id]=true;
    			else{
    				split(p[id].first,p[id].second);
    				int tmp=t[p[id].second].mn;
    				if(t[p[id].second].sz&1)res[i]++,res[min(t[tmp].val,t[id+n].val)]--;
    				if(p[id].first==p[id].second)continue;
    				if(t[tmp].val>=t[id+n].val)continue;
    				on[tmp-n]=false,cut(p[tmp-n].first,tmp),cut(p[tmp-n].second,tmp);
    				on[id]=true,link(p[id].first,id+n),link(p[id].second,id+n);
    			}
    		}
    		for(int j=0;j<fin[i].size();j++){
    			int id=fin[i][j];
    			if(on[id])cut(p[id].first,id+n),cut(p[id].second,id+n);
    		}
    		if(i)res[i]+=res[i-1];
    		puts(res[i]?"No":"Yes");
    	}
    	return 0;
    }
    

  • 相关阅读:
    3D 图片播放焦点图插件Adaptor
    深入浅出 RPC
    深入浅出 RPC
    Fragment学习(一) :生命周期
    Activity学习(二):Activity的启动模式(转载)
    Activity学习(一):生命周期
    AsyncTask的使用
    Handler用法总结
    深入理解java泛型
    mysql多表连接和子查询
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602086.html
Copyright © 2011-2022 走看看