codeforces 891C

给出一个无向图,每次询问图中的一个边集,是否存在包含这个边集的MST

考虑怎么判断是否存在包含某一条边的MST
若他不在原图的MST上,显然只能尝试用它替换一条权值相等的,MST上的边
设他的权值为c,可以先对权< c的边先做kruskal建出生成树,用这些边将原图缩点
然后若这条边在这个缩完点的图上不是自环,就合法

对于每个询问的边集,不同的边权显然互相独立
这个边集合法的充要条件是每条边合法且相同边权的边在它的那个图上不成环

将这些询问离线掉,按边权从小到大建出图,每一步后缩点,然后对于每个询问若他有当前权的边就判这些边是否成环
判环好像有一些高级的随机分配值用异或判的姿势但我不会
我们可以直接暴力的用并查集判环
但每个询问判完后要撤销操作所以写按秩合并的并查集,带个log

nlogn

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 510000;
const int maxm = 510000;
const int maxq = 510000;

int n,m,q;
struct edge{int i,x,y,c;}e[maxm]; int To[maxm];
inline bool cmp(const edge x,const edge y){return x.c<y.c;}

struct node{int i,j,x;};
inline bool operator <(const node x,const node y){return x.x>y.x;}
priority_queue<node>Q;
vector<int>V[maxq];
int ans[maxq];

int fa[maxn],siz[maxn];
int t[maxn][2],tp;
int findfa(const int x){return fa[x]==x?x:findfa(fa[x]);}
bool merge(int ei)
{
    int x=e[ei].x,y=e[ei].y;
    int f1=findfa(x),f2=findfa(y);
    if(f1==f2) return false;
    if(siz[f1]<siz[f2]) swap(f1,f2);
    fa[f2]=f1,siz[f1]+=siz[f2];
    t[++tp][0]=f1,t[tp][1]=f2;
    return true;
}
void gback()
{
    while(tp)
    {
        int x=t[tp][0],y=t[tp--][1];
        fa[y]=y; siz[x]-=siz[y];
    }
}

int main()
{
    read(n); read(m);
    for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].c),e[i].i=i;
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++) To[e[i].i]=i;

    read(q);
    for(int i=1;i<=q;i++)
    {
        ans[i]=0; int kk; read(kk);
        while(kk--) 
        {
            int cc; read(cc); cc=To[cc];
            V[i].push_back(cc);
        }kk=V[i].size();
        sort(V[i].begin(),V[i].end());
        Q.push((node){i,0,V[i][0]});
    }

    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;)
    {
        int j=i+1;for(;j<=m&&e[j].c==e[i].c;j++);j--;

        while(!Q.empty())
        {
            node now=Q.top(); if(now.x>j) break;
            Q.pop();
            for(;now.j<V[now.i].size()&&V[now.i][now.j]<=j;now.j++)
                if(!merge(V[now.i][now.j])) break;
            gback();
            if(now.j==V[now.i].size()) { ans[now.i]=1; continue; }
            now.x=V[now.i][now.j];
            if(now.x>j) Q.push(now);
        }

        for(;i<=j;i++) merge(i);
        tp=0;
    }

    for(int i=1;i<=q;i++) puts(ans[i]==1?"YES":"NO");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值