zoukankan      html  css  js  c++  java
  • Codeforces 891C Envy(MST + 并查集的撤销)

    题目链接  Envy

    题意  给出一个连通的无向图和若干询问。每个询问为一个边集。求是否存在某一棵原图的最小生成树包含了这个边集。

    考虑$kruskal$的整个过程,

    当前面$k$条边已经完成操作的时候(就是前$k$小的边已经进行并查集缩点,此时部分点已经形成了若干个连通块)

    这个时候突然冒出来一些权值相同并且这个权值大于前$k$条边最大权值的边,问这些边是否能同时被某一棵最小生成树包含。

    那我们依次检查这突然冒出来的几条边,在原来的这个并查集的基础上,继续进行缩点操作。

    如果这些边在处理的时候没有遇到某条边的两个点在连边之前已经连通的情况,那么这些边能同时被某一棵最小生成树包含。

    反之亦然。

    对于这道题我们要做的就是把所有询问离线,把每条询问边塞到对应的权值里面。

    当我现在要检查权值为$x$的某些同一个询问里的边的时候,首先要保证那些权值小于$x$的边都已经进行了并查集缩点。

    然后把这些权值为$x$的某些同一个询问里的边想象成刚刚说的“突然冒出来的几条边”,检查就可以了。

    如果不行的话这个询问的$id$的答案就是$NO$了。

    因为同一个权值里面可能会有(其实是一般都有)询问id不同的边,那么处理完某一批边之后我们要对询问的时候改动的并查集撤销。

    开个栈记录一下即可。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define	rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define	dec(i, a, b)	for (int i(a); i >= (b); --i)
    #define	MP		make_pair
    #define	fi		first
    #define	se		second
    
    typedef pair <int, int> PII;
    
    const int N = 5e5 + 10;
    
    struct node{
    	int x, y, z;
    	void scan(){ scanf("%d%d%d", &x, &y, &z);}
    } e[N];
    
    struct query{
    	int x, y, id;
    	friend bool operator < (const query &a, const query &b){
    		return a.id < b.id;
    	}
    };
    		
    int father[N], ret[N], n, m, qu, now, opnum;
    stack <PII> s;
    vector <query> v[N];
    vector <PII>   g[N];
    
    int getfather(int x){
    	if (father[x]){
    		s.push(MP(x, father[x]));
    		father[x] = getfather(father[x]);
    		return father[x];
    	}
    
    	else return x;
    }
    
    int gf(int x){ return father[x] ? father[x] = gf(father[x]) : x;}
    
    void work(int x, int y){
    	int fx = gf(x), fy = gf(y);
    	if (fx ^ fy) father[fx] = fy;
    }
    
    void solve(int cnt, int x, int y){
    	while (!s.empty()) s.pop();
    	rep(i, x, y){
    		int fx = getfather(v[cnt][i].x), fy = getfather(v[cnt][i].y);
    		if (fx == fy) ret[v[cnt][i].id] = 1;
    		else{
    			s.push(MP(fx, father[fx]));
    			father[fx] = fy;
    		}
    	}
    
    	while (!s.empty()){
    		father[s.top().fi] = s.top().se;
    		s.pop();
    	}
    }
    
    int main(){
    
    	scanf("%d%d", &n, &m);
    	rep(i, 1, m){
    		e[i].scan();
    		g[e[i].z].push_back(MP(e[i].x, e[i].y));
    	}
    
    	scanf("%d", &qu);
    	rep(i, 1, qu){
    		int opnum;
    		scanf("%d", &opnum);
    		rep(j, 1, opnum){
    			int x;
    			scanf("%d", &x);
    			v[e[x].z].push_back({e[x].x, e[x].y, i});
    		}
    	}
    
    	rep(i, 1, 5e5) sort(v[i].begin(), v[i].end());
    	rep(i, 1, 5e5){
    		for (auto u : g[i - 1]) work(u.fi, u.se);
    		int sz = v[i].size();
    		if (sz == 0) continue;
    		int now = 0;
    		while (now < sz){
    			int j = now;
    			while (j + 1 < sz && v[i][j + 1].id == v[i][j].id) ++j;
    			solve(i, now, j);
    			now = j + 1;
    		}
    	}
    
    	rep(i, 1, qu) puts(ret[i] ? "NO" : "YES");
    	return 0;
    }
    
  • 相关阅读:
    vue app项目 第一天 基本架构和路由配置
    uni-app真机调试报错request:fail abort解决方法
    C#中的虚函数virtual
    ASP.NET Core中返回 json 数据首字母大小写问题
    ASP.NET Core中使用Cache缓存
    ASP.NET Core WebApi使用ActionFilterAttribute过滤器
    ASP.NET Core WebApi使用JWT认证
    微信小程序自动识别收货地址
    开发常用网站
    微信小程序自定义导航栏组件
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/8022924.html
Copyright © 2011-2022 走看看