Envy CodeForces - 892E

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值