zoukankan      html  css  js  c++  java
  • CF938G Shortest Path Queries 和 CF576E Painting Edges

    这两道都用到了线段树分治和按秩合并可撤销并查集。

    Shortest Path Queries

    给出一个连通带权无向图,边有边权,要求支持 q 个操作:

    1. x y d 在原图中加入一条 x 到 y 权值为 d 的边
    2. x y 把图中 x 到 y 的边删掉
    3. x y 表示询问 x 到 y 的异或最短路

    保证任意操作后原图连通无重边自环且操作均合法

    n,m,q≤200000

    题解

    WC2011 最大XOR和路径一样,先考虑没有加边删边的做法

    1. 做出原图的任意一棵生成树
    2. 把每个非树边和树边形成的环丢进线性基里
    3. 询问时把两点在树上的路径异或和丢进线性基里求最小异或和

    有加边删边呢?线段树分治即可。

    每到一个点,把边都连上,然后分治左右。退出时撤销即可。

    需要用到按秩合并并查集以支持快速查询和撤销操作。并查集在合并两个点时,将儿子节点压入栈,撤销时直接把儿子节点的贡献删除即可。

    #include<bits/stdc++.h>
    using namespace std;
    template<class T> T read(){
        T x=0,w=1;char c=getchar();
        for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*w;
    }
    template<class T> T read(T&x){
        return x=read<T>();
    }
    #define co const
    #define il inline
    typedef long long LL;
    
    co int N=200000+10;
    struct edge {int u,v,w,l,r;}e[N<<1];
    
    vector<edge> g[N<<2];
    map<pair<int,int>,int> mp;
    int val[N],fa[N],siz[N];
    int stk[N<<1],top;
    #define lc (x<<1)
    #define rc (x<<1|1)
    void update(int x,int l,int r,co edge&e){
    	if(e.l>e.r) return; // edit 1
    	if(e.l<=l&&r<=e.r)
    		return g[x].push_back(e);
    	int mid=(l+r)>>1;
    	if(e.l<=mid) update(lc,l,mid,e);
    	if(e.r>mid) update(rc,mid+1,r,e);
    }
    
    struct ask {int u,v;}qst[N];
    int bas[20][30];
    
    int find_fa(int x){
    	return fa[x]==x?x:find_fa(fa[x]);
    }
    int find_val(int x){
    	return fa[x]==x?0:val[x]^find_val(fa[x]);
    }
    void insert(int x,int dep){
    	for(int i=0;i<(int)g[x].size();++i){
    		int u=g[x][i].u,v=g[x][i].v,w=g[x][i].w;
    		int fu=find_fa(u),fv=find_fa(v);
    		if(fu!=fv){
    			if(siz[fu]>siz[fv]) swap(fu,fv),swap(u,v);
    			fa[fu]=fv,siz[fv]+=siz[fu],val[fu]=find_val(u)^find_val(v)^w;
    			stk[++top]=fu;
    		}
    		else{
    			w^=find_val(u)^find_val(v);
    			for(int j=29;j>=0;--j)if(w>>j&1){
    				if(!bas[dep][j]) {bas[dep][j]=w;break;}
    				w^=bas[dep][j];
    			}
    		}
    	}
    }
    void reset(int to){
    	for(;top>to;--top){
    		int u=stk[top];
    		siz[fa[u]]-=siz[u],val[u]=0,fa[u]=u;
    	}
    }
    void solve(int x,int l,int r,int dep){
    	int tmp=top;
    	insert(x,dep);
    	if(l==r){
    		int u=qst[l].u,v=qst[l].v;
    		int ans=find_val(u)^find_val(v);
    		for(int i=29;i>=0;--i)if(ans>>i&1)
    			if(bas[dep][i]) ans^=bas[dep][i];
    		printf("%d
    ",ans);
    		return reset(tmp);
    	}
    	int mid=(l+r)>>1;
    	copy(bas[dep],bas[dep]+30,bas[dep+1]),solve(lc,l,mid,dep+1);
    	copy(bas[dep],bas[dep]+30,bas[dep+1]),solve(rc,mid+1,r,dep+1);
    	reset(tmp);
    }
    
    int main(){
    	int n=read<int>(),m=read<int>();
    	int ecnt=0;
    	for(int i=1;i<=m;++i){
    		int u=read<int>(),v=read<int>(),w=read<int>();
    		e[++ecnt]=(edge){u,v,w,1,-1},mp[make_pair(u,v)]=ecnt;
    	}
    	int q=read<int>();
    	int totq=0;
    	for(int i=1;i<=q;++i){
    		int opt=read<int>();
    		if(opt==1){ // add
    			int x=read<int>(),y=read<int>(),d=read<int>();
    			e[++ecnt]=(edge){x,y,d,totq+1,-1},mp[make_pair(x,y)]=ecnt;
    		}
    		else if(opt==2){ // remove
    			int x=read<int>(),y=read<int>();
    			e[mp[make_pair(x,y)]].r=totq,mp[make_pair(x,y)]=0;
    		}
    		else{ // calculate
    			int x=read<int>(),y=read<int>();
    			qst[++totq]=(ask){x,y};
    		}
    	}
    	for(int i=1;i<=ecnt;++i){
    		if(e[i].r==-1) e[i].r=totq;
    		update(1,1,totq,e[i]);
    	}
    	for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
    	solve(1,1,totq,0);
    	return 0;
    }
    

    我写的线段树是需要判断插入合不合法的,好几次因为这个RE了。

    Painting Edges

    BZOJ4025 二分图类似,这道题可以用并查集来维护奇环。

    但是这道题我们并不知道每条边的具体存在时间,因此我们这样:
    假设每次修改都生效,我们把每条边的第一种颜色的存在时间加入分治结构(其实就是线段树)
    DFS的过程中,如果DFS到一个叶节点时,判断当前修改能否执行,这样我们就得到了这条边在下一个时间段的颜色,然后将这条边的下一个时间段加入分治结构

    时间复杂度O(q log q log n)。话说我这两道题写的并查集都是假的也没人来卡我。

    #include<bits/stdc++.h>
    using namespace std;
    template<class T> T read(){
        T x=0,w=1;char c=getchar();
        for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*w;
    }
    template<class T> T read(T&x){
        return x=read<T>();
    }
    #define co const
    #define il inline
    typedef long long LL;
    
    co int N=500000+10;
    int n,m,K,Q;
    struct edge {int u,v,c;}e[N];
    struct ask {int e,c,r;}q[N];
    int last[N];
    
    int stk[N],col[N],top;
    struct Disj{
    	int fa[N],val[N],siz[N];
    	
    	int find_fa(int x){
    		return fa[x]==x?x:find_fa(fa[x]);
    	}
    	int find_val(int x){
    		return fa[x]==x?0:val[x]^find_val(fa[x]);
    	}
    	void merge(int c,int x,int y){
    		int fx=find_fa(x),fy=find_fa(y);
    		if(fx==fy) return;
    		if(siz[fx]>siz[fy]) swap(fx,fy),swap(x,y);
    		fa[fx]=fy,siz[fy]+=siz[fx],val[fx]=find_val(x)^1^find_val(y);
    		stk[++top]=fx,col[top]=c;
    	}
    }d[51];
    void reset(int tmp){
    	for(;top>tmp;--top){
    		int u=stk[top],c=col[top];
    		d[c].siz[d[c].fa[u]]-=d[c].siz[u],d[c].fa[u]=u,d[c].val[u]=0;
    	}
    }
    
    vector<pair<int,int> > g[N<<2];
    #define lc (x<<1)
    #define rc (x<<1|1)
    void insert(int x,int l,int r,int ql,int qr,co pair<int,int>&v){
    	if(ql>qr) return;
    	if(ql<=l&&r<=qr)
    		return g[x].push_back(v);
    	int mid=(l+r)>>1;
    	if(ql<=mid) insert(lc,l,mid,ql,qr,v);
    	if(qr>mid) insert(rc,mid+1,r,ql,qr,v);
    }
    void solve(int x,int l,int r){
    	int tmp=top;
    	for(int i=0;i<(int)g[x].size();++i){
    		int e=g[x][i].first,c=g[x][i].second;
    		d[c].merge(c,::e[e].u,::e[e].v);
    	}
    	if(l==r){
    		int u=e[q[l].e].u,v=e[q[l].e].v,c=q[l].c;
    		if(d[c].find_fa(u)!=d[c].find_fa(v) or d[c].find_val(u)!=d[c].find_val(v))
    			puts("YES"),e[q[l].e].c=q[l].c;
    		else puts("NO");
    		insert(1,1,Q,l+1,q[l].r,make_pair(q[l].e,e[q[l].e].c));
    		return reset(tmp);
    	}
    	int mid=(l+r)>>1;
    	solve(lc,l,mid),solve(rc,mid+1,r);
    	reset(tmp);
    }
    
    int main(){
    	read(n),read(m),read(K),read(Q);
    	for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v);
    	for(int i=1;i<=Q;++i){
    		int id=read<int>(),c=read<int>();
    		if(last[id]) q[last[id]].r=i-1;
    		q[i]=(ask){id,c,Q},last[id]=i;
    	}
    	for(int c=1;c<=K;++c)
    		for(int i=1;i<=n;++i) d[c].fa[i]=i,d[c].siz[i]=1;
    	solve(1,1,Q);
    	return 0;
    }
    
  • 相关阅读:
    emacs 探索之六:latex中文支持
    One网络模拟器探索之六:Report类的扩展
    emacs 探索之五:latex配置
    emacs 探索之三:基本操作
    DataSet数据传送性能比较
    SQL 2008 附加数据库报5120的错误的解决办法
    软件工程师不可不知的10个概念
    在日期上加上相应天数,并在GridView上显示
    SQL 跨表更新
    SQLSERVER 处理两个日期相减
  • 原文地址:https://www.cnblogs.com/autoint/p/11495714.html
Copyright © 2011-2022 走看看