http://codeforces.com/problemset/problem/892/E
看着别人题解写的 算是学了一波可撤销并查集
结论 对于一条权值为w的边来说 如果在所有权值小于w的边全部加入图后这条边的两个端点还没有联通的话 那么这条边能出现在最小生成树中
对于单组询问来说 可以根据上述结论来判断 这里的多组询问可以离线处理 不太好处理的就是权值相同的边 首先按权值其次是查询的下标排序 对于某边 前面同值不同询问的边必须要从并查集拿掉 这就要用到可撤销并查集 级记录一个时间戳打标记 不属于当前询问则从并查集剔除
#include <bits/stdc++.h>
using namespace std;
struct node1
{
int u;
int v;
int w;
};
struct node2
{
int u;
int v;
int w;
int id;
int val;
};
node1 pre[500010];
node2 order[500010];
int f1[500010],f2[500010],cnt[500010],ans[500010];
int n,m,q,tot,cur;
bool cmpI(node1 n1,node1 n2)
{
return n1.w<n2.w;
}
bool cmpII(node2 n1,node2 n2)
{
if(pre[n1.val].w==pre[n2.val].w) return n1.id<n2.id;
else return pre[n1.val].w<pre[n2.val].w;
}
int getfI(int p)
{
if(f1[p]==p) return p;
else
{
f1[p]=getfI(f1[p]);
return f1[p];
}
}
int getfII(int p)
{
if(cnt[p]!=cur) f2[p]=f1[p],cnt[p]=cur;
if(f2[p]==p) return p;
else
{
f2[p]=getfII(f2[p]);
return f2[p];
}
}
int main()
{
int k,i,j,fu,fv,pos;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++) scanf("%d%d%d",&pre[i].u,&pre[i].v,&pre[i].w);
scanf("%d",&q);
for(i=1;i<=q;i++)
{
scanf("%d",&k);
while(k--)
{
tot++;
order[tot].id=i;
scanf("%d",&order[tot].val);
order[tot].u=pre[order[tot].val].u,order[tot].v=pre[order[tot].val].v,order[tot].w=pre[order[tot].val].w;
}
}
sort(order+1,order+tot+1,cmpII);
sort(pre+1,pre+m+1,cmpI);
for(i=1;i<=n;i++) f1[i]=i,f2[i]=i;
pos=1,cur=1;
for(i=1,j=1;i<=tot;i=pos)
{
while(j<=m&&pre[j].w<order[i].w)
{
fu=getfI(pre[j].u);
fv=getfI(pre[j].v);
if(fu!=fv) f1[fv]=fu;
j++;
}
while(pos<=tot&&order[pos].w==order[i].w)
{
if(pos==i||order[pos-1].id!=order[pos].id) cur++;
fu=getfII(order[pos].u);
fv=getfII(order[pos].v);
if(fu!=fv) f2[fv]=fu;
else ans[order[pos].id]=1;
pos++;
}
}
for(i=1;i<=q;i++)
{
if(ans[i]==0) printf("YES\n");
else printf("NO\n");
}
return 0;
}