CodeForces892E 可撤销并查集/最小生成树
题意:给出一个 n 个点 m 条边的无向图,每条边有边权,共 Q 次询问,每次给出 ki 条边,问这些边能否同时在一棵最小生成树上。
这题乍一看看不出什么方法来,仔细一想发现除了确实看不出什么东西来。
但是我们从求最小生成树的kruskal算法里面可以发现,尽管一个图可以有多个最小生成树,但是想要把一条边替换掉领一条边变成最小生成树一定是两条边的权值相同。
所以说每一条权值不同的边是互相独立的,如果比这条边权值小的边全部联系起来之后这条边两端是联通的,说明这条边是不合法的。
这是考虑边是否可以加入最小生成树的情况,但是他给了很多条边,也就是说每次除了判断这条边的合法性之外还要对每一个询问单独判断,也就是判环。
怎么判环是个问题,但是对每个询问都建图判环肯定是不现实的,比较可行的说法是用并查集,将比所有w的边小的边联通之后判断他们能否联通而不形成环,注意到Q的数据是50w,每次都重新建一个并查集也不太现实,这时候就又要考虑掏出一手可撤销并查集。也就是对每一次的Union的两者存到一个栈里,如果要撤销这些操作就从栈顶弹出元素重新独立,对于不需要撤销的操作就直接不用入栈了,可撤销的并查集是不能路径压缩的,只能上一波按秩合并看上去好像优化了很多的样子。
因此,我们对于每一个询问用并查集判断一下可行性之后撤销,就很帅。
因此我们得出了一个具体的做法
先将所有的edge排序,但是由于给你的query是边的编号,排序之后编号就变动了,需要用一个ID数组来存储排序前的编号在排序后变成的编号
然后将所有的query存储下来,由于排序后的编号是按照权值递增的,直接按照编号排序就可以达到query内递增的效果。
如果每一个权值都暴力的跑一边每个询问,是无疑会TLE的,一个好的方法是用优先队列存储当前询问的状态,队内的结点存储当前询问的编号w,当前询问到了这个询问里数字编号,当前询问的这个数,优先队列的排列按w从小到大递增
然后对于每次出现过的w,就判断队列中是否有符合要求的询问,如果这个询问问到了头还没出错,他的ans就是1,如果在中间遇到出错了,ans就为0并且出队
经过测验,这样写可以优化很多。
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const int MAXBUF=;char buf[MAXBUF],*ps=buf,*pe=buf+;
inline bool isdigit(const char& n) {return (n>=''&&n<='');}
inline void rnext(){if(++ps==pe)pe=(ps=buf)+fread(buf,sizeof(char),sizeof(buf)/sizeof(char),stdin);}
template <class T> inline bool in(T &ans){
#ifdef VSCode
ans=;T f=;register char c;
do{c=getchar();if ('-'==c)f=-;}while(!isdigit(c)&&c!=EOF);
if(c==EOF)return false;do{ans=(ans<<)+(ans<<)+c-;
c=getchar();}while(isdigit(c)&&c!=EOF);ans*=f;return true;
#endif
#ifndef VSCode
ans =;T f=;if(ps==pe)return false;do{rnext();if('-'==*ps)f=-;}
while(!isdigit(*ps)&&ps!=pe);if(ps==pe)return false;do{ans=(ans<<)+(ans<<)+*ps-;
rnext();}while(isdigit(*ps)&&ps!=pe);ans*=f;return true;
#endif
}const int MAXOUT=; //*(int(*)[10])p
char bufout[MAXOUT], outtmp[],*pout = bufout, *pend = bufout+MAXOUT;
inline void write(){fwrite(bufout,sizeof(char),pout-bufout,stdout);pout = bufout;}
inline void out_char(char c){*(pout++)=c;if(pout==pend)write();}
inline void out_str(char *s){while(*s){*(pout++)=*(s++);if(pout==pend)write();}}
template <class T>inline void out_int(T x) {if(!x){out_char('');return;}
if(x<)x=-x,out_char('-');int len=;while(x){outtmp[len++]=x%+;x/=;}outtmp[len]=;
for(int i=,j=len-;i<j;i++,j--) swap(outtmp[i],outtmp[j]);out_str(outtmp);}
template<typename T, typename... T2>
inline int in(T& value, T2&... value2) { in(value); return in(value2...); }
#define For(i, x, y) for(register int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
#define Vec Point
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = 5e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K,Q;
struct Edge{int u,v,w,id;}edge[maxn];
struct node{
int w,id,pointer;
friend bool operator < (node a,node b){
return a.w > b.w;
}
};
VI query[maxn];
priority_queue<node> P;
int Back[maxn][];
int cnt;
int tree[maxn];
int Size[maxn];
int ans[maxn],ID[maxn];
void init(){For(i,,N) tree[i] = i,Size[i] = ;}
int find(int p){
if(p == tree[p]) return p;
return find(tree[p]);
}
bool Union(int u,int v){
u = find(u); v = find(v);
if(u == v) return false;
if(Size[u] > Size[v]) swap(u,v);
tree[u] = v; Size[v] += Size[u];
Back[++cnt][]= u; Back[cnt][] = v;
return true;
}
void Cancel(){
while(cnt){
int u = Back[cnt][]; int v = Back[cnt--][];
tree[u] = u; Size[v] -= Size[u];
}
}
bool cmp(Edge a,Edge b) {return a.w < b.w;}
int main()
{
in(N,M);
init();
For(i,,M) in(edge[i].u,edge[i].v,edge[i].w),edge[i].id = i;
sort(edge + ,edge + M + ,cmp);
For(i,,M) ID[edge[i].id] = i;
in(Q);
For(i,,Q){
in(tmp);
while(tmp--){
int t; in(t); t = ID[t];
query[i].pb(t);
}
sort(query[i].begin(),query[i].end());
node h; h.id = i; h.pointer = ; h.w = query[i][];
P.push(h);
}
for(int i = ; i <= M;){
int W = edge[i].w;
int j=i+;for(;j<=M&&edge[j].w==edge[i].w;j++);j--;
while(!P.empty()){
node x = P.top();
if(x.w > j) break;P.pop();
int v = x.id;
while(x.pointer < query[v].size() && query[v][x.pointer] <= j){
if(!Union(edge[query[v][x.pointer]].u,edge[query[v][x.pointer]].v)) break;
x.pointer++;
}
Cancel();
if(x.pointer == query[v].size()) {
ans[x.id] = ;
continue;
}
x.w = query[v][x.pointer];
if(x.w > j) P.push(x);
}
for(;i<=j;i++) Union(edge[i].u,edge[i].v);
cnt = ;
}
For(i,,Q){
if(ans[i] == ) puts("YES");
else puts("NO");
}
#ifdef VSCode
write();
system("pause");
#endif
return ;
}
CodeForces892E 可撤销并查集/最小生成树的更多相关文章
- codeforces 892E(离散化+可撤销并查集)
题意 给出一个n个点m条边的无向联通图(n,m<=5e5),有q(q<=5e5)个询问 每个询问询问一个边集{Ei},回答这些边能否在同一个最小生成树中 分析 要知道一个性质,就是权值不同 ...
- bzoj2049 线段树 + 可撤销并查集
https://www.lydsy.com/JudgeOnline/problem.php?id=2049 线段树真神奇 题意:给出一波操作,拆边加边以及询问两点是否联通. 听说常规方法是在线LCT, ...
- BZOJ4358: permu(带撤销并查集 不删除莫队)
题意 题目链接 Sol 感觉自己已经老的爬不动了.. 想了一会儿,大概用个不删除莫队+带撤销并查集就能搞了吧,\(n \sqrt{n} logn\)应该卡的过去 不过不删除莫队咋写来着?....跑去学 ...
- 【离线 撤销并查集 线段树分治】bzoj1018: [SHOI2008]堵塞的交通traffic
本题可化成更一般的问题:离线动态图询问连通性 当然可以利用它的特殊性质,采用在线线段树维护一些标记的方法 Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常 ...
- 【Codeforces576E_CF576E】Painting Edges(可撤销并查集+线段树分治)
题目 CF576E 分析: 从前天早上肝到明天早上qwq其实颓了一上午MC ,自己瞎yy然后1A,写篇博客庆祝一下. 首先做这题之前推荐一道很相似的题:[BZOJ4025]二分图(可撤销并查集+线段树 ...
- 【BZOJ4025】二分图(可撤销并查集+线段树分治)
题目: BZOJ4025 分析: 定理:一个图是二分图的充要条件是不存在奇环. 先考虑一个弱化的问题:保证所有边出现的时间段不会交叉,只会包含或相离. 还是不会?再考虑一个更弱化的问题:边只会出现不会 ...
- 算法笔记--可撤销并查集 && 可持久化并查集
可撤销并查集模板: struct UFS { stack<pair<int*, int>> stk; int fa[N], rnk[N]; inline void init(i ...
- 2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树
目录 题意: 分析: @(2019牛客暑期多校训练营(第八场)E_Explorer) 题意: 链接 题目类似:CF366D,Gym101652T 本题给你\(n(100000)\)个点\(m(1000 ...
- Codeforces 938G 线段树分治 线性基 可撤销并查集
Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问 ...
随机推荐
- Scrum Meeting NO.7
Scrum Meeting No.7 1.会议内容 经过老师提醒,我们认识到,应尽快把主要功能实现,其他的细枝末节应在这之后慢慢添加.当今最重要的任务是和online组和数据处理组实现数据共享. 此外 ...
- JAVA程序设计 实验一报告
北京电子科技学院(BESTI) 实 验 报 告 课程:Java程序设计 班级:1351 姓名:李畅宇 学号:20135129 成绩: 指导教师:娄嘉鹏 ...
- 同步手绘板——android端取色
作为绘图软件,颜色的选取必不可少,在刚开始取色时,所选颜色和显示颜色始终不一致,比如选取白色显示绿色,在这个问题上消耗了太多的时间,后来发现是比例问题,通过修改实现恰当的取色.
- Python 中的字符串(str)、字典(dict)详解及操作方法
一.字符串 在python中字符串是一种重要数据类型.其他数据类型分别为: 数字-number -------- int.long.float.complex这几种 字符串-string ------ ...
- Kitematic - VirtualBox is not installed. Docker for windows 10
Kitematic - VirtualBox is not installed. Docker for windows 10 https://github.com/docker/kitematic/i ...
- pgm12
作为 inference 部分的小结,我们这里对 machine learning 里面常见的三个 model 的 inference 问题进行整理,当然很幸运的是他们都存在 tractable 的算 ...
- ceph PG数量调整/PG的状态说明
优化: PG Number PG和PGP数量一定要根据OSD的数量进行调整,计算公式如下,但是最后算出的结果一定要接近或者等于一个2的指数.调整PGP不会引起PG内的对象的分裂,但是会引起PG的分布的 ...
- BZOJ1415[Noi2005]聪聪和可可——记忆化搜索+期望dp
题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...
- BZOJ3724 PA2014Final Krolestwo(欧拉回路+构造)
如果没有长度为偶数的限制,新建一个点向所有奇点连边,跑欧拉回路即可,显然此时一定存在欧拉回路,因为所有点度数都为偶数. 考虑长度为偶数的限制,将每个点拆成两个点放进一个二分图里,那么每条原图中的边在二 ...
- day5 用户交互 input用法
death_age = 80 name= input("your name:") age= input("your age:") #inputs 接受的所有数据 ...