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\n",ans[i]?"NO":"YES");
	return 0;
}
posted @ 2017-12-19 16:29  yuanyuan-97  阅读(958)  评论(0编辑  收藏  举报