[CF891C] Envy - Kruskal,并查集

给出一个 n 个点 m条边的无向图,每条边有边权,共 Q次询问,每次给出 \(k\)条边,问这些边能否同时在一棵最小生成树上。

Solution

所有最小生成树中某权值的边的数量是一定的

加完小于某权值的所有边后图的连通性是一样的

对于每个询问,每种权值分开考虑

对每个权值,加完小于这条边的权值后的所有边

然后判断这个权值在缩点后图上是否成环

因此需要跑一次 Kruskal 并且记录下对于每条边,加完权值小于它的所有边后,其两个端点所在的连通块编号

这样询问时只需要拿着并查集搞就可以了

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;

struct edge {
    int u,v,w,id,bu,bv;
    bool operator < (const edge &b) const {
        return w < b.w;
    }
} e[N];

bool cmp(const edge &a, const edge &b) {
    return a.id < b.id;
}

int n,m,q,t1,t2,t3,f[N];

int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
void merge(int i,int j) {if(find(i)!=find(j)) f[find(i)]=find(j); }

int solve(vector <edge> v) {
    /*cout<<"solve"<<endl;
    for(int i=0;i<v.size();i++) cout<<v[i].bu<<" "<<v[i].bv<<" "<<v[i].w<<endl;
    cout<<"-----"<<endl;*/
    map<int,int> mp;
    for(int i=0;i<v.size();i++) mp[v[i].bu]++, mp[v[i].bv]++;
    int ind=0;
    for(map<int,int>::iterator it=mp.begin();it!=mp.end();it++)
        it->second = ++ind;
    for(int i=0;i<v.size();i++) v[i].bu=mp[v[i].bu], v[i].bv=mp[v[i].bv];
    for(int i=1;i<=ind;i++) f[i]=i;
    int flag=1;
    for(int i=0;i<v.size();i++) {
        if(find(v[i].bu)==find(v[i].bv)) flag=0;
        merge(v[i].bu,v[i].bv);
    }
    return flag;
}

int main() {
    ios::sync_with_stdio(false);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        e[i].id=i;
    }
    sort(e+1,e+m+1);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++) {
        if(e[i].w != e[i-1].w) {
            int pos=i;
            while(e[pos].w == e[pos+1].w && pos<m) ++pos;
            for(int j=i;j<=pos;j++) e[j].bu=find(e[j].u), e[j].bv=find(e[j].v);
        }
        merge(e[i].u,e[i].v);
    }
    scanf("%d",&q);
    sort(e+1,e+m+1,cmp);
    //for(int i=1;i<=m;i++) cout<<e[i].bu<<" "<<e[i].bv<<endl;

    for(int i=1;i<=q;i++) {
        int tot;
        scanf("%d",&tot);
        vector <edge> v;
        for(int j=1;j<=tot;j++) {
            int tmp;
            scanf("%d",&tmp);
            v.push_back(e[tmp]);
        }
        sort(v.begin(),v.end());
        v.push_back((edge){0,0,0});
        int flag = 1;
        for(int j=0;j<tot;j++) {
            int pos=j;
            while(v[j].w == v[j+1].w && pos<tot-1) ++pos;
            vector <edge> vv;
            for(int k=j;k<=pos;k++) vv.push_back(v[k]);
            flag &= solve(vv);
            j=pos;
        }
        if(flag) puts("YES");
        else puts("NO");
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/12318154.html

时间: 2024-07-31 17:12:15

[CF891C] Envy - Kruskal,并查集的相关文章

SDUT 2933-人活着系列之Streetlights(最小生成树Kruskal+并查集实现)

人活着系列之Streetlights Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 人活着如果是为了家庭,亲情----可以说是在这个世界上最温暖人心的,也是最让人放不下的,也是我在思索这个问题最说服自己接受的答案.对,也许活着是一种责任,为了繁殖下一代,为了孝敬父母,男人要养家糊口,女人要生儿育女,就这样循环的过下去,但最终呢?还是劳苦愁烦,转眼成空呀! 为了响应政府节约能源的政策,某市要对路灯进行改革,已知该市有n个城镇,

Connect the Campus (Uva 10397 Prim || Kruskal + 并查集)

题意:给出n个点的坐标,要把n个点连通,使得总距离最小,可是有m对点已经连接,输入m,和m组a和b,表示a和b两点已经连接. 思路:两种做法.(1)用prim算法时,输入a,b.令mp[a][b]=0.然后进行一遍prim(2)Kruskal算法+并查集 代码: //prim写法 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <

codevs 1078最小生成树 Kruskal+并查集

题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场.为了使花费最少,他想铺设最短的光纤去连接所有的农场. 你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案. 每两个农场间的距离不会超过100000 输入描述 Input Description 第一行: 农场的个数,N(3<=N<=100).

最小生成树(kruskal+并查集)

最小生成树 最小生成树即用最少的边权将所有给定的点连在同一联通分量中,常用kruskal和prim算法 kruskal算法(适合稀疏图) 最小生成树的kruskal算法,稍带并查集的应用 int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); //不要漏了fa[x]=... } int kruskal() { int ans=0; for(int i=0;i<N;i++) fa[i]=i;//初始化并查集 sort(edge,edge+e);

POJ 3723 Conscription (Kruskal并查集求最小生成树)

Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14661   Accepted: 5102 Description Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to b

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然

poj 3625 Building Roads 最小生成树(prime或kruskal+并查集)(算法归纳)

Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Description Farmer John had just acquired several new farms! He wants to connect the farms with roads so that he can travel from any farm to any other farm via a sequence of roads;

UVa 1395 苗条的生成树(Kruskal+并查集)

https://vjudge.net/problem/UVA-1395 题意: 给出一个n结点的图,求苗条度(最大边减最小边的值)尽量小的生成树. 思路: 主要还是克鲁斯卡尔算法,先仍是按权值排序,对于一个连续的边集区间[L,R],如果这些边使得n个点全部连通,则一定存在一个苗条度不超过W[R]-W[L]的生成树.从小到大枚举L,对于每个L,从小到大枚举R. 这道题目我一直超时,最后发现数组开小了,我一直以为数组开小了肯定会出来Runtime error的... 1 #include<iostr

灯泡游戏 (Kruskal)(并查集)

灯泡游戏 时间限制: 1 Sec  内存限制: 64 MB提交: 9  解决: 4[提交][状态][讨论版] 题目描述 有 一个n行m列的矩阵,左上角坐标是(0,0),右下角坐标是(n-1,m-1).每个格子有一个字符, “0”至“9”表示数字0至9,“a”至“z”表示数字10至35,“A”至“Z”表示数字36至61.矩阵的每个格子都有一个灯泡,刚开始除了左上角的 灯泡是亮的,其他的灯泡都是灭的,刚开始你的得分是0.游戏的过程是这样的:每次选一个灯泡是亮的格子X,同时选一个灯泡是灭的格子Y,而且