• 【HNOI2010】-城市建设(动态最小生成树)


    传送门

    大意:给你一棵树,每次永久修改一条边权,询问当前最小生成树

    做法真的妙妙的

    考虑到无论我们怎么修改某条边的边权

    都总有一些边可以被选到

    也总有一些边不会被选到

    因此就引入了两个操作


    Contraction:Contraction:
    我们将当前分治区间的所有要修改的边全部赋为inf-inf

    那么显然当前如果在MSTMST中的边肯定会在MSTMST

    那我们就可以把这些边缩成一个点来缩小图的规模


    ReductionReduction

    同理将这些边赋为infinf

    那么此时不在MSTMST中的边肯定不会在MSTMST

    那这些边我们就可以弃掉不要了


    注意理解我们在分治的每一层都进行了这个操作

    这样减少的点是逐渐递进的

    而这样到最后l=rl=r的时候就已经只剩会影响的边了

    便可以快速统计答案了

    复杂度O(nlog2n)O(nlog^2{n})

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    inline int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    	return res*f;
    }
    const int N=50005;
    const int Log=20;
    const ll inf=0x3f3f3f3f;
    int m,n,k,ans[N],fa[N],num[Log],a[N],rk[N],p[N];
    struct edge{
    	int u,v,val,pos;
    }e[Log][N],q1[N],q2[N];
    inline bool comp(const edge &a,const edge &b){
    	return a.val<b.val;
    }
    struct ask{
    	int pos,val;
    	ll ans;
    }q[N];
    inline void init(int tot){
    	for(int i=1;i<=tot;i++){
    		int u=q1[i].u,v=q1[i].v;
    		fa[u]=u,fa[v]=v,rk[u]=rk[v]=1;
    	}
    }
    int find(int x){
    	return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    inline void merge(int u,int v){
    	if(rk[u]<=rk[v])fa[u]=v,rk[v]+=rk[u];
    	else fa[v]=u,rk[u]+=rk[v];
    }
    inline void contraction(int &tot,ll &now){
    	int cnt=0;
    	init(tot);
    	sort(q1+1,q1+tot+1,comp);
    	for(int i=1;i<=tot;i++){
    		int u=q1[i].u,v=q1[i].v;
    		if(find(u)!=find(v))
    			merge(fa[u],fa[v]),q2[++cnt]=q1[i];
    	}
    	for(int i=1;i<=cnt;i++){
    		int u=q2[i].u,v=q2[i].v;
    		fa[u]=u,fa[v]=v,rk[u]=rk[v]=1;
    	}
    	for(int i=1;i<=cnt;i++){
    		int u=q2[i].u,v=q2[i].v;
    		if(q2[i].val!=-inf&&find(u)!=find(v))
    			merge(fa[u],fa[v]),now+=q2[i].val;
    	}
    	cnt=0;
    	for(int i=1;i<=tot;i++){
    		int u=q1[i].u,v=q1[i].v;
    		if(find(u)!=find(v)){
    			q2[++cnt]=q1[i],p[q1[i].pos]=cnt;
    			q2[cnt].u=fa[q2[cnt].u];
    			q2[cnt].v=fa[q2[cnt].v];
    		}
    	}
    	for(int i=1;i<=cnt;i++)q1[i]=q2[i];
    	tot=cnt;
    }
    inline void reduction(int &tot){
    	int cnt=0;
    	init(tot);
    	sort(q1+1,q1+tot+1,comp);
    	for(int i=1;i<=tot;i++){
    		int u=q1[i].u,v=q1[i].v;
    		if(find(u)!=find(v)){
    			merge(fa[u],fa[v]);
    			q2[++cnt]=q1[i],p[q1[i].pos]=cnt;
    		}
    		else if(q1[i].val==inf)q2[++cnt]=q1[i],p[q1[i].pos]=cnt;
    	}
    	for(int i=1;i<=cnt;i++)q1[i]=q2[i];
    	tot=cnt;
    }
    #define mid ((l+r)>>1)
    void cdq(int l,int r,int dep,ll now){
    	int tot=num[dep];
    	if(l==r)a[q[l].pos]=q[l].val;
    	for(int i=1;i<=tot;i++)e[dep][i].val=a[e[dep][i].pos];
    	for(int i=1;i<=tot;i++)q1[i]=e[dep][i],p[q1[i].pos]=i;
    	if(l==r){
    		q[l].ans=now;
    		init(tot);
    		sort(q1+1,q1+tot+1,comp);
    		for(int i=1;i<=tot;i++){
    			int u=q1[i].u,v=q1[i].v;
    			if(find(u)!=find(v))
    				q[l].ans+=q1[i].val,merge(fa[u],fa[v]);
    		}
    		return;
    	}
    	for(int i=l;i<=r;i++)q1[p[q[i].pos]].val=-inf;
    	contraction(tot,now);
    	for(int i=l;i<=r;i++)q1[p[q[i].pos]].val=inf;
    	reduction(tot);
    	for(int i=1;i<=tot;i++)e[dep+1][i]=q1[i];
    	num[dep+1]=tot;
    	cdq(l,mid,dep+1,now);
    	cdq(mid+1,r,dep+1,now);
    }
    #undef mid
    int main(){
    	n=read(),m=read(),k=read();
    	for(int i=1;i<=m;i++){
    		int u=read(),v=read();a[i]=read();
    		e[0][i]=(edge){u,v,a[i],i};
    	}
    	for(int i=1;i<=k;i++){
    		q[i].pos=read(),q[i].val=read();
    	}
    	num[0]=m;
    	cdq(1,k,0,0);
    	for(int i=1;i<=k;i++){
    		cout<<q[i].ans<<'
    ';
    	}
    	return 0;
    }
    
  • 相关阅读:
    分布式事务-第一刀
    Qt
    自描述C++部分面试题集
    读书笔记6.21
    STL vector容器 和deque容器
    C++ STL框架
    C++ 多态
    C++ 虚继承
    C++ 类的继承和派生
    C++ 类中的函数重载
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366357.html
Copyright © 2020-2023  润新知