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;
}