• NOIP 2013货车运输


    当然这题有很多做法,但是我看到没有人写DSU的很惊奇

    按照之前做连双向边题的经验,这题可以用并查集维护联通

    然后对于每个询问\(x,y\),考虑启发式合并

    当两个点集\(x,y\)合并时,一些涉及到其中点的询问可以被解决,而遍历\(x,y\)中的询问集其实是等价的,所以可以直接用启发式合并存下这个点集涉及到的询问,在合并时我们要遍历数组,所以可以同时完成对于询问的回答

    typedef long long ll;
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    char IO;
    int rd(){
    	int s=0,f=0;
    	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    const int N=1e5+10;
    
    int n,m,q;
    struct Edge{
    	int u,v,x;
    	void Get(){ u=rd(),v=rd(),x=rd(); }
    	bool operator < (const Edge __) const {
    		return x>__.x;
    	}
    }e[N];
    int ans[N];
    struct Query{
    	int x,id;
    };
    vector <Query> V[N];
    int fa[N];
    int Find(int x){ return fa[x]==x?x:fa[x]=Find(fa[x]);}
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n) fa[i]=i;
    	rep(i,1,m) e[i].Get();
    	sort(e+1,e+m+1);
    	rep(i,1,q=rd()) {
    		ans[i]=-1;
    		int x=rd(),y=rd();
    		V[x].push_back((Query){y,i});
    		V[y].push_back((Query){x,i});
    	}
    	rep(i,1,m) {
    		int x=Find(e[i].u),y=Find(e[i].v);
    		if(x==y) continue;
    		if(V[x].size()>V[y].size()) swap(x,y);
    		fa[x]=y;
    		rep(j,0,V[x].size()-1) {
    			int t=V[x][j].x,id=V[x][j].id;
    			if(Find(t)==y) {
    				ans[id]=max(ans[id],e[i].x);
    			} else V[y].push_back(V[x][j]);
    		}
    	}
    	rep(i,1,q) printf("%d\n",ans[i]);
    }
    
    
    

    非常精简

    如果你不懂启发式合并的原理,我可以简单证明一下

    对于这些集合,元素总数为m

    每一次我们将小的集合合并到大的上面,集合大小至少是两倍,所以每个元素最多会在log2(m)次合并中被访问到

    总复杂度\(q \cdot log(q)\)

  • 相关阅读:
    数据结构实验之栈四:括号匹配
    数据结构实验之栈三:后缀式求值
    8月7日学习
    8月6日学习
    8月5日学习
    8月4日学习
    周总结
    8月3日学习
    8月2日学习
    8月1日学习
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11332444.html
Copyright © 2020-2023  润新知