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. Being a (amateurish) team:团队开发体会

    0x00 Being a (amateurish) team This is the process of changing hydrogen into breathable oxygen, and ...

  2. Zookeeper 3.4.8分布式安装

    1.机器信息 五台centos 64位机器 2.集群规划 Server Name Hadoop Cluster Zookeeper   Ensemble HBase Cluster Hadoop01 ...

  3. Jquery画折线图、柱状图、饼图

    1.今天做了一个折线图,首先需要导js文件.这里有一个demo:http://files.cnblogs.com/files/feifeishi/jquery_zhexiantubingtuzhuzh ...

  4. 如何实现基于ssh框架的投票系统的的质量属性

    如何实现基于ssh框架的投票系统的的质量属性: 项目 :网上考试系统 我做的是网上考试系统,因为标准化的考试越来越重要,而通过计算机进行标准化判卷,系统会自动判卷出成绩,组织考试的人不用组织人员打印试 ...

  5. 20150421 作业5 四则运算 测试与封装 5.1 5.2(doing)

    结伴队友:王佳寧,他的博客地址:http://www.cnblogs.com/paopaotai/ 5.2 黑白盒測試 測試項目名稱 黑盒測試 測試人員 葉子鵬&王佳寧 測試編號 測試頁面 測 ...

  6. centos7编译安装zabbix的错误

    [Z3001] connection to database 'zabbix' failed: [2002] Can't connect to local MySQL server through s ...

  7. Beta阶段敏捷冲刺四

    一.举行站立式会议 1.当天站立式会议照片一张 2.团队成员报告 林楚虹 (1) 昨天已完成的工作:导入到数据表 (2) 今天计划完成的工作:排行榜功能 (3) 工作中遇到的困难:转为csv文件时音标 ...

  8. Activiti的部署问题

    http://www.kafeitu.me/activiti/2012/03/22/workflow-activiti-action.html 既可以通过每次Spring应用程序启动时,执行部署命令. ...

  9. ERP开源框架 + 二次开发平台 介绍

    经历了多年软件开发,深受网络大侠们的资源共享才得以有所成绩, 本人主要是做企业ERP软件,一直有个感受,开发具体某个功能不难,但随着需求的增加,管理庞大的代码却成了最大的问题 而为企业管理所做的开发, ...

  10. python之tkinter使用-Grid(网格)布局管理器

    # 使用tkinter编写登录窗口 # Grid(网格)布局管理器会将控件放置到一个二维的表格里,主控件被分割为一系列的行和列 # stricky设置对齐方式,参数N/S/W/E分别表示上.下.左.右 ...