• 【BZOJ2238】—MST(树链剖分+最小生成树)


    传送门

    大意:给定一个图,每次求删去一条边后的MSTMST

    先做一次最小生成树

    考虑到如果当前删除的不是MSTMST上的边

    则对答案没有影响

    而如果我们删去边(u,v)(u,v)MSTMST新加进了一条边(x,y)(x,y)

    (x,y)(x,y)必定和未删去时的MSTMST构成一个环

    而且(u,v)(u,v)必定在这个环里

    换句话说

    删去边(u,v)(u,v)后只有可能加进满足这样条件的(x,y)(x,y),而且肯定是加valval最小的一条

    那我们考虑对每一条不在最小生成树的边维护一下它可能成为删去哪些边的答案

    很明显是这条边和当前MSTMST上构成的环

    而考虑到每条边都是取最小的

    就把边权转成点权

    每个非树边给环上的点取个minmin

    用线段树维护一下区间最小、区间取minmin就可以了

    #include<bits/stdc++.h>
    using namespace std;
    #define gc getchar
    inline int read(){
    	int res=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while((isdigit(ch)))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    	return res;
    }
    const int N=100005;
    int n,m,adj[N],nxt[N<<1],to[N<<1],c[N],ans,q,cnt;
    char op[4];
    namespace Seg{
    	#define lc (u<<1)
    	#define rc ((u<<1)|1)
    	#define mid ((l+r)>>1)
    	const int inf=1e9;
    	int tr[N<<2],tag[N<<2],idx[N<<2];
    	inline void pushup(int u){
    		tr[u]=min(tr[lc],tr[rc]);
    	}
    	inline void pushdown(int u){
    		if(tag[u]==inf)return;
    		tag[lc]=min(tag[lc],tag[u]),tag[rc]=min(tag[rc],tag[u]);
    		tr[lc]=min(tr[lc],tag[u]),tr[rc]=min(tr[rc],tag[u]),tag[u]=inf;
    	}
    	void buildtree(int u,int l,int r){
    		tag[u]=inf;
    		if(l==r){
    			tr[u]=inf;return;
    		}
    		buildtree(lc,l,mid);
    		buildtree(rc,mid+1,r);
    		pushup(u);
    	}
    	void update(int u,int l,int r,int st,int des,int k){
    		if(st<=l&&r<=des){
    			tr[u]=min(tr[u],k),tag[u]=min(tag[u],k);return;
    		}
    		pushdown(u),pushup(u);
    		if(st<=mid)update(lc,l,mid,st,des,k);
    		if(mid<des)update(rc,mid+1,r,st,des,k);
    	}
    	int query(int u,int l,int r,int st,int des){
    		if(st<=l&&r<=des){
    			return tr[u];
    		}
    		pushdown(u),pushup(u);
    		int res=inf;
    		if(st<=mid)res=min(res,query(lc,l,mid,st,des));
    		if(mid<des)res=min(res,query(rc,mid+1,r,st,des));
    		return res;
    	}
    }
    using namespace Seg;
    namespace SLPF{
    	int pos[N],top[N],fa[N],siz[N],son[N],dep[N],tot;
    	inline void addedge(int u,int v){
    		nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
    	}
    	void dfs1(int u){
    		siz[u]=1;
    		for(int e=adj[u];e;e=nxt[e]){
    			int v=to[e];
    			if(v==fa[u])continue;
    			fa[v]=u,dep[v]=dep[u]+1;
    			dfs1(v),siz[u]+=siz[v];
    			if(siz[v]>siz[son[u]])son[u]=v;
    		}
    	}
    	void dfs2(int u,int tp){
    		pos[u]=++tot,idx[tot]=u,top[u]=tp;
    		if(!son[u])return;
    		dfs2(son[u],tp);
    		for(int e=adj[u];e;e=nxt[e]){
    			int v=to[e];
    			if(v==fa[u]||v==son[u])continue;
    			dfs2(v,v);
    		}
    	}
    	inline void pathupdate(int u,int v,int w){
    		while(top[u]!=top[v]){
    			if(dep[top[u]]<dep[top[v]])swap(u,v);
    			update(1,1,n,pos[top[u]],pos[u],w);
    			u=fa[top[u]];
    		}
    		if(dep[u]<dep[v])swap(u,v);
    		update(1,1,n,pos[v]+1,pos[u],w);//注意边转点pos+1
    	}
    	inline int pathquery(int u,int v){//其实可以直接写成2个点单点查值,但本人懒癌晚期直接贴之前的板子了
    		int res=inf;
    		while(top[u]!=top[v]){
    			if(dep[top[u]]<dep[top[v]])swap(u,v);
    			res=min(res,query(1,1,n,pos[top[u]],pos[u]));
    			u=fa[top[u]];
    		}
    		if(dep[u]<dep[v])swap(u,v);
    		res=min(res,query(1,1,n,pos[v]+1,pos[u]));//注意边转点pos+1
    		return res;
    	}
    }
    using namespace SLPF;
    namespace Kruscal{
    	struct edge{
    		int u,v,w,id,vis;
    	}e[N];
    	int fa[N];
    	inline bool comp(const edge &a,const edge &b){
    		return a.w<b.w;
    	}
    	inline bool cmp(const edge &a,const edge &b){
    		return a.id<b.id;
    	}
    	int find(int x){
    		return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
    	}
    	inline int kruscal(){
    		sort(e+1,e+m+1,comp);int res=0,num=0;
    		for(int i=1;i<=n;i++)fa[i]=i;
    		for(int i=1;i<=m;i++){
    			int f1=find(e[i].u),f2=find(e[i].v);
    			if(f1!=f2){
    				fa[f2]=f1,res+=e[i].w,e[i].vis=1;num++;
    				addedge(e[i].u,e[i].v),addedge(e[i].v,e[i].u);
    			}
    		}
    		sort(e+1,e+m+1,cmp);
    		if(num<n-1)return -1;
    		return res;
    	}
    }
    using namespace Kruscal;
    int main(){
    	n=read(),m=read();
    	for(int i=1;i<=m;i++){
    		e[i].u=read(),e[i].v=read(),e[i].w=read(),e[i].id=i;
    	}
    	ans=kruscal();
    	dfs1(1),dfs2(1,1),buildtree(1,1,n);
    	for(int i=1;i<=m;i++){
    		if(!e[i].vis)
    		pathupdate(e[i].u,e[i].v,e[i].w);
    	}
    	q=read();
    	for(int i=1;i<=q;i++){
    		if(ans==-1){
    			cout<<"Not connected"<<'
    ';continue;
    		}
    		int x=read();
    		if(!e[x].vis){cout<<ans<<'
    ';continue;}
    		int tmp=pathquery(e[x].u,e[x].v);
    		if(tmp==inf)cout<<"Not connected"<<'
    ';
    		else cout<<(ans+tmp-e[x].w)<<'
    ';
    	}
    }
    
  • 相关阅读:
    git版本控制入门
    SpringBoot入门基础:编写HelloWorld(三)
    SpringBoot入门基础:构建SpringBoot项目及启动器讲解(二)
    SpringBoot入门基础:介绍(一)
    maven入门基础:为项目分配独立的仓库实践(十七)
    maven入门基础:nexus的权限管理(十六)
    maven入门基础:使用maven部署构件到nexus(十五)
    maven入门基础:配置maven从nexus下载构件(十四)
    maven入门基础:nexus构建搜索(十三)
    事务的写法
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366349.html
Copyright © 2020-2023  润新知