E. Envy(可撤销并查集+离线处理)

这篇博客讨论了在连接的无向加权图中,最小生成树(MST)的概念及其在解决图论问题中的重要性。文章指出,当在图上运行MST算法时,可能会导致某些边变得'嫉妒',因为它们没有被选入MST。接下来,博主提出了一个包含多个查询的问题,每个查询涉及图中的一组边,要求判断是否存在一个MST包含这些边。题目给出了图的顶点数、边数和查询数,并对边的权重和重复性进行了描述。对于每个查询,需要确定是否存在包含这些边的MST,并分别以'YES'或'NO'作答。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Envy
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

For a connected undirected weighted graph G, MST (minimum spanning tree) is a subgraph of G that contains all of G's vertices, is a tree, and sum of its edges is minimum possible.

You are given a graph G. If you run a MST algorithm on graph it would give you only one MST and it causes other edges to become jealous. You are given some queries, each query contains a set of edges of graph G, and you should determine whether there is a MST containing all these edges or not.

Input

The first line contains two integers n, m (2  ≤ n, m  ≤ 5·105, n - 1 ≤ m) — the number of vertices and edges in the graph and the number of queries.

The i-th of the next m lines contains three integers ui, vi, wi (ui ≠ vi, 1 ≤ wi ≤ 5·105) — the endpoints and weight of the i-th edge. There can be more than one edges between two vertices. It's guaranteed that the given graph is connected.

The next line contains a single integer q (1 ≤ q ≤ 5·105) — the number of queries.

q lines follow, the i-th of them contains the i-th query. It starts with an integer ki (1 ≤ ki ≤ n - 1) — the size of edges subset and continues with ki distinct space-separated integers from 1 to m — the indices of the edges. It is guaranteed that the sum of ki for 1 ≤ i ≤ q does not exceed 5·105.

Output

For each query you should print "YES" (without quotes) if there's a MST containing these edges and "NO" (of course without quotes again) otherwise.

Example
Input
5 7
1 2 2
1 3 2
2 3 1
2 4 1
3 4 1
3 5 2
4 5 2
4
2 3 4
3 3 4 5
2 1 7
2 1 2
Output
YES
NO
YES
NO
思路:首先我们知道肯定去判断询问中的边是不是多余的边,多余的边的按照最小生成树的算法,我们只用考虑边权小与当前边所构成的森林,如果加入这条边成环就是多余的,
这题还有一个情况,边权相同,都不是多余的边,就是不能出现在同一个mxt中这种我们就把这些同时加进去看会不会出现环,再把每次判断加进去的撤销就行了,用栈模拟下
ac代码:
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f
using namespace std;
const int maxn  = 5e5+5;
int fa[maxn];
int sz[maxn];
int n,m;
struct node
{
    int u,v,w;
}E[maxn];
struct node2
{
    int id,e;
};
struct node3
{
    int id,s,t;
};
stack<pair<int,int> >S;
vector<node>E2[maxn];
vector<node3>Q2[maxn];
vector<node2>Q[maxn];
int cmp(node2 a,node2 b)
{
    return E[a.e].w<E[b.e].w;
}
int ans[maxn];
int Find(int x)
{
  //  cout<<x<<endl;
    return x==fa[x]?x:Find(fa[x]);
}
void join(int x,int y)
{
    if(sz[x]<=sz[y]){
        fa[x] = y;
        sz[y]+=sz[x];
        S.push(make_pair(x,y));
    }
    else{
        fa[y] = x;
        sz[x] += sz[y];
        S.push(make_pair(y,x));
    }
}
void rem()
{
    pair<int,int> tmp = S.top();
    //cout<<tmp.first<<endl;
    S.pop();
    fa[tmp.first] = tmp.first;
    sz[tmp.second] -= sz[tmp.first];
}
int check(node3 tmp)
{
    //cout<<"fwfwf"<<endl;
    int cnt = 0;
    int ret = 1;
  //  cout<<"gan"<<' '<<tmp.s<<' '<<tmp.t<<endl;
    for(int i = tmp.s;i<=tmp.t;i++){
        int u = E[Q[tmp.id][i].e].u;
        int v = E[Q[tmp.id][i].e].v;
        int uu = Find(u);
        int vv = Find(v);
        if(uu==vv){
            ret= 0;
        }
        else{
    //            cout<<"unio1:"<<u<<' '<<v<<endl;
                cnt++;
            join(uu,vv);
        }
    }
   // cout<<"gaaaaaaaaaaaa"<<endl;
    //cout<<tmp.s<<' '<<tmp.t<<' '<<S.size()<<endl;
    for(int i = 0;i<cnt;i++){
       rem();
    }
    //cout<<"gggggggg"<<endl;
    return ret;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 0;i<=maxn-1;i++){
            E2[i].clear();
            Q2[i].clear();
        }
        for(int i = 0;i<=n;i++) fa[i] = i,sz[i] = 1;
        memset(ans,0,sizeof(ans));
        while(!S.empty()) S.pop();
        for(int i = 1;i<=m;i++){
            scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
            E2[E[i].w].push_back(E[i]);
        }
        int q;
        scanf("%d",&q);
        for(int i = 1;i<=q;i++){
            int c;
            scanf("%d",&c);
            while(c--){
                int x;
                scanf("%d",&x);
                node2 tmp;
                tmp.id = i;
                tmp.e =x;
                Q[i].push_back(tmp);
            }
            sort(Q[i].begin(),Q[i].end(),cmp);
            int s = 0;
            for(int j = 1;j<Q[i].size();j++){
                if(E[Q[i][j].e].w!=E[Q[i][j-1].e].w){
                    node3 tmp;
                    tmp.id = i;
                    tmp.s = s;
                    tmp.t = j-1;
                    s = j;
                    Q2[E[Q[i][j-1].e].w].push_back(tmp);
                }
            }
             node3 tmp;
                    tmp.id = i;
                    tmp.s = s;
                    tmp.t = Q[i].size()-1;
                    Q2[E[Q[i][Q[i].size()-1].e].w].push_back(tmp);
        }
        for(int i = 1;i<=500000;i++){
               // cout<<"fffffffffff"<<endl;
            for(int j = 0;j<Q2[i].size();j++){
                if(!check(Q2[i][j])){
                    ans[Q2[i][j].id] = 1;
             //   cout<<"result:"<<Q[Q2[i][j].id][Q2[i][j].t].e<<' '<<i<<endl;
                }
            }
            for(int j = 0;j<E2[i].size();j++){
                int u = Find(E2[i][j].u);
                int v = Find(E2[i][j].v);
                if(u!=v){
                   // cout<<"unio2:"<<E2[i][j].u<<' '<<E2[i][j].v<<endl;
                    join(u,v);
                }
            }
        }
        for(int i = 1;i<=q;i++){
            if(ans[i]){
                    puts("NO");
            }
            else{
                puts("YES");
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值