Skip to content

线段树分治 ​

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

题意:

一条边只会在某个时间段出现

询问每个时间段是否是二分图

思路:

二分图存在等于是否有奇数环,可以使用拓展域并查集判断

cpp
struct DSU{
    vector<int> par,siz;
    stack<int> st;
    int tot=0;
    DSU(){}
	DSU(int n){
		init(n);
	}
    void init(int n){
        par.resize(n);
        siz.assign(n,1);
        iota(par.begin(),par.end(),0);
    }
    int find(int x){
        return par[x]==x?x:find(par[x]);
    }
    bool merge(int x,int y,int op){
        x=find(x),y=find(y);
        if(x==y)return false;
        if(siz[x]<siz[y])swap(x,y);
        par[y]=x;
        siz[x]+=siz[y];
        if(op){
            st.push(y);
            tot++;
        }
        return true;
    }
	bool same(int x,int y){
		return find(x)==find(y);
	}
    void del(){
        if(!tot)return;
        tot--;
        int y=st.top();
        st.pop();
        siz[par[y]]-=siz[y];
        par[y]=y;
    }
    void back(int t=0){
        while(tot>t)del();
    }
} dsu;
int n;
struct SegmentTree{
	struct Node{
		int l,r;
		vector<array<int,2>> a;
	};
	vector<Node> t;
	void init(int n){
		t=vector<Node>(n<<2);
	}
	void build(int p,int l,int r){
		t[p]={l,r};
		if(l==r)return;
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
	}
	void modify(int p,int l,int r,int x,int y){
		if(l<=t[p].l&&r>=t[p].r){
			t[p].a.push_back({x,y});
			return;
		}
		int mid=t[p].l+t[p].r>>1;
		if(l<=mid)modify(p<<1,l,r,x,y);
		if(r>mid)modify(p<<1|1,l,r,x,y);
	}
	void dfs(int p,int l,int r){
		bool g=0;
		int tot=dsu.tot;
		for(auto &[u,v]:t[p].a){
			if(dsu.same(u,v)){
				for(int i=l;i<=r;i++)cout<<"No\n";
				g=1;
				break;
			}
			dsu.merge(u,v+n,1);
			dsu.merge(v,u+n,1);
		}
		if(!g){
			if(l==r){
				cout<<"Yes\n";
				return;
			}
			int mid=l+r>>1;
			dfs(p<<1,l,mid);
			dfs(p<<1|1,mid+1,r);
		}
		dsu.back(tot);
	}	
} seg;
void solve(){
	int m,k;
	cin>>n>>m>>k;
	seg.init(k);
	seg.build(1,1,k);
	dsu.init(2*n+1);
	for(int i=1;i<=m;i++){
		int x,y,l,r;
		cin>>x>>y>>l>>r;
		seg.modify(1,l+1,r,x,y);
	}
	seg.dfs(1,1,k);
}

CF1140F ​

题意:

CF2069F ​

题意:

给定两个图A,B,每次操作可以删除或增加A,B的边,求每次询问后,A需要添加多少条边才能使得,B是A的子图

思路:

考虑A,B的联合图,那么A和B都是联合图的子图,答案即是A和联合图的连通块个数差

问题转化成求动态维护连通块问题,考虑线段树分治+可撤销并查集离线求解

cpp
struct DSU{
    vector<int> par,siz;
    stack<int> st;
    int tot=0,cnt;
    DSU(){}
	DSU(int n){
		init(n);
	}
    void init(int n){
        back();
        cnt=n-1;
        par.resize(n);
        siz.assign(n,1);
        iota(par.begin(),par.end(),0);
    }
    int find(int x){
        return par[x]==x?x:find(par[x]);
    }
    bool merge(int x,int y,int op){
        x=find(x),y=find(y);
        if(x==y)return false;
        cnt--;
        if(siz[x]<siz[y])swap(x,y);
        par[y]=x;
        siz[x]+=siz[y];
        if(op){
            st.push(y);
            tot++;
        }
        return true;
    }
	bool same(int x,int y){
		return find(x)==find(y);
	}
    void del(){
        if(!tot)return;
        tot--;
        int y=st.top();
        st.pop();
        siz[par[y]]-=siz[y];
        int p=par[y];
        par[y]=y;
        if(find(p)!=find(y))cnt++;
    }
    void back(int t=0){
        while(tot>t)del();
    }
} dsu;
struct SegmentTree{
    struct Node{
        int l,r;
        vector<PII> v;
    };
    vector<Node> t;
    SegmentTree(){}
    SegmentTree(int n){
        init(n);
    }
    void init(int n){
        t=vector<Node>(n<<2);
    }
    void build(int p,int l,int r){
        t[p]={l,r};
        if(l==r)return;
        int mid=l+r>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
    }
    void modify(int p,int l,int r,int x,int y){
        if(l<=t[p].l&&t[p].r<=r){
            t[p].v.push_back({x,y});
            return;
        }
        int mid=t[p].l+t[p].r>>1;
        if(l<=mid)modify(p<<1,l,r,x,y);
        if(r>mid)modify(p<<1|1,l,r,x,y);
    }
    void dfs(int p,vector<int> &ans){
        int ts=dsu.tot;
        for(auto [x,y]:t[p].v){
            dsu.merge(x,y,1);
        }
        if(t[p].l==t[p].r){
            ans[t[p].l]=dsu.cnt;
        }
        else{
            dfs(p<<1,ans);
            dfs(p<<1|1,ans);
        }
        dsu.back(ts);
    }
} seg;
void cal(vector<array<int,4>> &v,vector<int> &ans,int n,int time){
    if(v.size()==0){
        ans.assign(time+1,n);
    }
    dsu.init(n+1);
    seg.init(time+1);
    seg.build(1,1,time);
    for(auto [l,r,x,y]:v){
        seg.modify(1,l,r,x,y);
    }
    seg.dfs(1,ans);
}
void solve(){
    int n,q;
    cin>>n>>q;
    map<PII,int> mp[2];
    vector<array<int,4>> e[2];
    for(int i=1;i<=q;i++){
        char c;
        int x,y;
        cin>>c>>x>>y;
        if(x>y)swap(x,y);
        if(mp[c-'A'].count({x,y})){
            e[c-'A'].push_back({mp[c-'A'][{x,y}],i-1,x,y});
            mp[c-'A'].erase({x,y});
        }
        else mp[c-'A'][{x,y}]=i;
    }
    for(int i=0;i<2;i++){
        for(auto [p,t]:mp[i]){
            e[i].push_back({t,q,p.first,p.second});
        }
    }
    vector<int> ans(q+1),res(q+1);
    for(auto it:e[0])e[1].push_back(it);
    cal(e[0],ans,n,q);
    cal(e[1],res,n,q);
    for(int i=1;i<=q;i++)cout<<ans[i]-res[i]<<"\n";
}