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. Alpha冲刺之事后诸葛亮

    组长博客 作业博客 项目Postmortem 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件针对的是福大学子来到食堂会犹豫不决无法决定吃什么 ...

  2. mybatis逆向工程生成example的问题

    如果不想生成example,在 targetRuntime="MyBatis3" 处修改为  targetRuntime="MyBatis3Simple"  & ...

  3. Analyze a docker instance start failure

      错误信息:Cannot start container xxxxxxxxxxx | Error getting container xxxxxxxxxxxxxxx  from driver dev ...

  4. Laravel 常见错误 1071 Specified key was too long

    Laravel 5.5 + Mysql 5.5 ,执行 migrate 时,提示索引长度超过指定的 1000 bytes 原因: Mysql 对索引有一定的长度限制,版本不同长度不同: MyIsAm ...

  5. Java微信二次开发(六)

    Token定时获取 需要导入库:添加log4j(slf4j-api-1.5.10.jar,slf4j-log4j12-1.5.10.jar,log4j-1.2.15.jar,并且在src下添加log4 ...

  6. js Dom 编程

    1. 节点及其类型:  1). 元素节点  2). 属性节点: 元素的属性, 可以直接通过属性的方式来操作.  3). 文本节点: 是元素节点的子节点, 其内容为文本.   2. 在 html 文档的 ...

  7. ajax 提交数组 泛型集合(二)

    最近在项目中,使用 mvc架构,model层使用code first 碰见一个问题,前台json传递数据给后台action的复杂对象,发现复杂对象中的list范型集合并没有获取到数据. 研究半天,终于 ...

  8. Django实现websocket完成实时通讯,聊天室,在线客服等

    一 什么是Websocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在WebS ...

  9. Spring AOP动态代理原理与实现方式

    AOP:面向切面.面向方面.面向接口是一种横切技术横切技术运用:1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   2.日志处理:3.安全验证 ...

  10. Android 判断是否有声音在播放

    在Android中,我们可以通过AudioManager来判断是否有声音在播放. 实例1: 源码地址: PhoneWindowManager.java (frameworks\base\policy\ ...