http://codeforces.com/problemset/problem/892/E

题意:给出一个 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 可撤销并查集/最小生成树的更多相关文章

  1. codeforces 892E(离散化+可撤销并查集)

    题意 给出一个n个点m条边的无向联通图(n,m<=5e5),有q(q<=5e5)个询问 每个询问询问一个边集{Ei},回答这些边能否在同一个最小生成树中 分析 要知道一个性质,就是权值不同 ...

  2. bzoj2049 线段树 + 可撤销并查集

    https://www.lydsy.com/JudgeOnline/problem.php?id=2049 线段树真神奇 题意:给出一波操作,拆边加边以及询问两点是否联通. 听说常规方法是在线LCT, ...

  3. BZOJ4358: permu(带撤销并查集 不删除莫队)

    题意 题目链接 Sol 感觉自己已经老的爬不动了.. 想了一会儿,大概用个不删除莫队+带撤销并查集就能搞了吧,\(n \sqrt{n} logn\)应该卡的过去 不过不删除莫队咋写来着?....跑去学 ...

  4. 【离线 撤销并查集 线段树分治】bzoj1018: [SHOI2008]堵塞的交通traffic

    本题可化成更一般的问题:离线动态图询问连通性 当然可以利用它的特殊性质,采用在线线段树维护一些标记的方法 Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常 ...

  5. 【Codeforces576E_CF576E】Painting Edges(可撤销并查集+线段树分治)

    题目 CF576E 分析: 从前天早上肝到明天早上qwq其实颓了一上午MC ,自己瞎yy然后1A,写篇博客庆祝一下. 首先做这题之前推荐一道很相似的题:[BZOJ4025]二分图(可撤销并查集+线段树 ...

  6. 【BZOJ4025】二分图(可撤销并查集+线段树分治)

    题目: BZOJ4025 分析: 定理:一个图是二分图的充要条件是不存在奇环. 先考虑一个弱化的问题:保证所有边出现的时间段不会交叉,只会包含或相离. 还是不会?再考虑一个更弱化的问题:边只会出现不会 ...

  7. 算法笔记--可撤销并查集 && 可持久化并查集

    可撤销并查集模板: struct UFS { stack<pair<int*, int>> stk; int fa[N], rnk[N]; inline void init(i ...

  8. 2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树

    目录 题意: 分析: @(2019牛客暑期多校训练营(第八场)E_Explorer) 题意: 链接 题目类似:CF366D,Gym101652T 本题给你\(n(100000)\)个点\(m(1000 ...

  9. Codeforces 938G 线段树分治 线性基 可撤销并查集

    Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问 ...

随机推荐

  1. [2019BUAA软件工程]第1次阅读作业

    [2019BUAA软件工程]第1次阅读作业 Tips Link 作业连接 [2019BUAA软件工程]第1次阅读作业 读<构建之法>的疑惑 个人开发流程(Personal Software ...

  2. Linux内核分析——程序破解

    1. 掌握NOP.JNE.JE.JMP.CMP汇编指令的机器码 NOP:NOP指令即“空指令”.执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令.(机器码: ...

  3. 《Linux内核分析》第八周学习笔记

    <Linux内核分析>第八周学习笔记 进程的切换和系统的一般执行过程 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163 ...

  4. 读<架构漫谈>系列有感

    读了这一系列博文,我对架构也有了大致的了解.在简单的阅读之后,我解决了几个问题. 第一个问题,什么是架构? 要学习架构,首先要知道架构.那么,什么是架构呢?引用<架构漫谈(一)>里的话就是 ...

  5. "一个程序员的生命周期"读后感

    这篇文章中作者叙述了自己和大多数大学生或许都会面对的问题,即是会走过挺多的歪路,面临很多的困难和压力,但是作者却从未放弃自己真正追求的东西.对于一个过来人的经验之谈,我们应该吸取经验,在大学好好去奋斗 ...

  6. 【Alpha阶段】展示博客发布!

    1.团队成员简介 Email:qianlxc@126.com Free time:8:00 7:00 a.m ~ 11:00 12:00p.m Introduction: 我是一个热情的人.开朗的人. ...

  7. python 中的三元表达式及lambda

    一.三元表达式 举一个简单的列子,很多地方都有这样的规定,比如用水或者用电,假设用水价格为3R/立方米,当你每个月用超过7立方米后,超出的水按照3.3R/立方米计价.然后写一个程序计算一个家庭每月的水 ...

  8. Maven的课堂笔记3

    8 仓库管理 仓库可以分为三种:1.本地仓库(本机).2.私服(公司局域网内的maven服务器).3.中央仓库(互联上,例如 struts2官网,或者hibernate官网) 可以根据maven坐标定 ...

  9. Issue: business key in a call activiti

    https://community.alfresco.com/thread/221280-business-key-in-a-call-activity 这个帖子有一些讨论和回复. https://c ...

  10. Character Encoding Issues for tomcat

    https://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q8 https://stackoverflow.com/questions/10936846 ...