zoukankan      html  css  js  c++  java
  • codeforces 891C Envy

    codeforces 891C Envy

    来源

    http://codeforces.com/problemset/problem/891/C

    题面

    给定一个带权无向图,每次询问一组边,问是否存在一个最小生成树包含询问的这些边。

    数据规模都是 (5e5)

    题解

    • 很容易证明:对于一组边,把它按照边权分成几个小组,如果每个小组都存在一个最小生成树包含这些边,那么就存在一个最小生成树包含这个组的所有边。
    • 对于一组边权相等的边,如何判断是否存在一个最小生成树包含它们?
      • 假设这组边边权为w,我们用Kruskal算法求最小生成树时,做到边权为w的时候优先选择这组边,判断是否每条边都能加进去,就可以了。
    • 如果暴力求个 (q) 次最小生成树,复杂度就爆炸了,有没有复杂度低一点的做法呢?
    • 先介绍一下按秩合并可撤销并查集:
      • 不压缩路径,根节点记录树高。
      • 合并两棵树时,树高小的合并到树高大的。如果树高一样,新的树根高加一。
      • 考虑复杂度:
        • 如果树高为(1),至少需要一个结点。
        • 如果树高为(2),至少需要两个结点。
        • 如果树高为(3),至少需要四个结点。
        • 如果结点个数为(n)个,树高是(O(logn))级别的。
        • 因此查询某个结点的根节点时,向上走的步数是(O(logn))级别的。
        • 合并复杂度(O(1))
      • 如何撤销?
        • 把当前的操作存入栈,因为没有进行路径压缩,所以可以撤销。
    • 所以这道题离线处理,把组按照权值分成小组,小组加进去后再撤销,继续加下一个小组,就做完了。

    复杂度

    (O(nlogn))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define rep(i, a, b) for(int i=(a); i<(b); i++)
    #define sz(x) (int)x.size()
    #define de(x) cout<< #x<<" = "<<x<<endl
    #define dd(x) cout<< #x<<" = "<<x<<" "
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef vector<int> vi;
    //------
    
    const int N=505050;
    int n,m,q;
    int pre[N],rk[N];
    bool ans[N];
    pair<pii,int> e[N];
    vector<pii> ee[N],qq[N];
    
    int find(int x) {
    	if(x==pre[x]) return x;
    	return find(pre[x]);
    }
    void join(int x,int y) {
    	if(rk[x]==rk[y]) ++rk[x];
    	if(rk[x]<rk[y]) swap(x,y);
    	pre[y]=x;
    }
    
    void gao(int w,int l,int r) {
    	int ind=qq[w][l].fi;
    	vector<pair<pii,pii> > sta;
    	rep(i,l,r) {
    		int x=qq[w][i].se;
    		int u=e[x].fi.fi, v=e[x].fi.se;
    		int fu=find(u), fv=find(v);
    		if(fu==fv) {
    			ans[ind]=1;
    		} else {
    			if(rk[fu]<rk[fv]) swap(fu,fv);
    			sta.pb(mp(mp(fv,pre[fv]),mp(fu,rk[fu])));
    			if(rk[fu]==rk[fv]) ++rk[fu];
    			pre[fv]=fu;
    		}
    	}
    	for(int i=sz(sta)-1;i>=0;--i) {
    		pre[sta[i].fi.fi]=sta[i].fi.se;
    		rk[sta[i].se.fi]=sta[i].se.se;
    	}
    }
    
    int main() {
    	///
    	scanf("%d%d",&n,&m);
    	///init
    	rep(i,0,n+1) {
    		pre[i]=i;
    		rk[i]=1;
    	}
    	///read
    	rep(i,1,m+1) {
    		int u,v,w;scanf("%d%d%d",&u,&v,&w);
    		e[i]=mp(mp(u,v),w);
    		ee[w].pb(mp(u,v));
    	}
    	scanf("%d",&q);
    	rep(i,1,q+1) {
    		int k;scanf("%d",&k);
    		rep(j,0,k) {
    			int x;scanf("%d",&x);
    			qq[e[x].se].pb(mp(i,x));
    		}
    	}
    	///solve
    	rep(w,1,N) {
    		for(int l=0,r;l<sz(qq[w]);l=r) {
    			for(r=l;r<sz(qq[w])&&qq[w][l].fi==qq[w][r].fi;++r) ;
    			gao(w,l,r);
    		}
    		rep(i,0,sz(ee[w])) {
    			int u=ee[w][i].fi, v=ee[w][i].se;
    			int fu=find(u), fv=find(v);
    			if(fu!=fv) join(fu, fv);
    		}
    	}
    	///print
    	rep(i,1,q+1) printf("%s
    ",ans[i]?"NO":"YES");
    	return 0;
    }
    
  • 相关阅读:
    mac redis 安装及基本设置 python操作redis
    mac webstorm自动编译typescript配置
    MySQL数据库的基本操作
    python 面试基础考试题收集
    pyhon 列表的增删改查
    python 文件读取方法详解
    MAC下绕开百度网盘限速下载的方法,三步操作永久生效
    浏览器窗口输入网址后发生的一段事情(http完整请求)
    CMDB
    django适当进阶篇
  • 原文地址:https://www.cnblogs.com/wuyuanyuan/p/8066444.html
Copyright © 2011-2022 走看看