题目大意:

N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

题解:

这道题考试的时候没想出来

于是便爆炸

结果今天下午拿出昨天准备的题表准备做题的时候

题表里就有这题..

欲哭无泪啊有木有... ...


  • 说正经的

假设我们可以做到用并查集实现区间减法

那么很显然的做法就是维护前缀并查集然后做差

但是并查集并不满足区间减法.

但是我们可以考虑一下如果并查集满足区间减法,那我们会拿它做什么

我们对并查集做差实际上就是想使并查集\([1,R]\)退回到删除掉\([1,L-1]\)区间内的边的状态

这什么玩意 ?? 这也能搞 ???

上面的思路都是考虑一个区间内有多少边对这个答案造成了影响.

我们反过来考虑,考虑一下每条边会影响哪些答案

我们想象一下并查集维护的过程及其对答案的影响:

每次加入一条边,如果边的两端未连通,那么直接连接两点,那么对所有包括了这个点的区间都一定会使这个区间的答案-1.我们定义此影响的起始端点为0.

如果两条点已经连通,由于我们已经计算过之前连通这两个点的边对答案的贡献了(贡献一定由编号最小的边做出).所以我们知道这条边对答案造成影响当且仅当这个区间不包括编号最小的边且包括当前的边.所以定义此影响的起始端点为最小边的编号.

为了使下一次操作依然正确,我们将找到的编号最小的边删除,并将当前边加入.不难发现这样是正确的.

所以我们只要求出某个区间中影响答案的起始端点小于询问端点的边的数目即可。

至于后面的这个操作可以用可持久化线段树维护

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 200010;
const int inf = 0x3f3f3f3f;
namespace lct{
struct Node{
Node *ch[2],*fa;
int mn,w,tag;
inline void update();
inline void push_down();
}*null;
Node mem[maxn<<2],*it;
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = null->fa = null;
null->mn = null->w = inf;null->tag = 0;
}
inline Node* newNode(int val){
Node *p = it++;p->w = p->mn = val;
p->ch[0] = p->ch[1] = p->fa = null;
p->tag = 0;return p;
}
inline void Node::update(){
mn = min(min(ch[0]->mn,ch[1]->mn),w);
}
inline void Node::push_down(){
if(this == null || tag == 0) return;
if(ch[0] != null) ch[0]->tag ^= 1;
if(ch[1] != null) ch[1]->tag ^= 1;
swap(ch[0],ch[1]);tag = 0;
}
inline void rotate(Node *p,Node *x){
int k = p == x->ch[1];
Node *y = p->ch[k^1],*z = x->fa;
if(z->ch[0] == x) z->ch[0] = p;
if(z->ch[1] == x) z->ch[1] = p;
if(y != null) y->fa = x;
p->fa = z;p->ch[k^1] = x;
x->fa = p;x->ch[k] = y;
x->update();p->update();
}
inline bool isroot(Node *p){
return (p == null) || (p->fa->ch[0] != p && p->fa->ch[1] != p);
}
inline void Splay(Node *p){
p->push_down();
while(!isroot(p)){
Node *x = p->fa,*y = x->fa;
y->push_down();x->push_down();p->push_down();
if(isroot(x)) rotate(p,x);
else if((x->ch[0] == p)^(y->ch[0] == x)) rotate(p,x),rotate(p,y);
else rotate(x,y),rotate(p,x);
}p->update();
}
inline Node* Access(Node *x){
for(Node *y = null;x != null;y=x,x=x->fa)
Splay(x),x->ch[1] = y,x->update();
}
inline void makeroot(Node *p){
Access(p);Splay(p);p->tag ^= 1;
}
inline void link(Node *x,Node *y){
makeroot(x);x->fa = y;
}
inline void cut(Node *x,Node *y){
makeroot(x);Access(y);Splay(y);
y->ch[0] = y->ch[0]->fa = null;
y->update();
}
inline Node* findroot(Node *x){
Access(x);Splay(x);
while(x->ch[0] != null) x = x->ch[0];
Splay(x);return x;
}
inline int query(Node *x,Node *y){
makeroot(x);Access(y);Splay(y);
return y->mn;
}
}
namespace seg{
struct Node{
Node* ch[2];
int num;
void update();
}*null;
Node mem[maxn*30],*it;
Node *root[maxn];
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = null;
null->num = 0;root[0] = null;
}
inline void Node::update(){
num = ch[0]->num + ch[1]->num;
}
Node* insert(Node *rt,int l,int r,int pos){
Node *p = it++;(*p) = (*rt);
if(l == r){
p->num ++;
return p;
}
int mid = (l+r) >> 1;
if(pos <= mid) p->ch[0] = insert(p->ch[0],l,mid,pos);
else p->ch[1] = insert(p->ch[1],mid+1,r,pos);
p->num = p->ch[0]->num + p->ch[1]->num;
return p;
}
int query(Node *x,Node *y,int l,int r,int L,int R){
if(L <= l && r <= R) return y->num - x->num;
int mid = (l+r) >> 1;
if(R <= mid) return query(x->ch[0],y->ch[0],l,mid,L,R);
if(L > mid) return query(x->ch[1],y->ch[1],mid+1,r,L,R);
return query(x->ch[0],y->ch[0],l,mid,L,R) + query(x->ch[1],y->ch[1],mid+1,r,L,R);
}
}
lct::Node* mp[maxn];
struct Node{
int u,v;
}e[maxn];
int main(){
lct::init();seg::init();
int n,m,q,type;read(n);read(m);read(q);read(type);
for(int i=1;i<=n;++i) lct::newNode(inf);
for(int i=1;i<=m;++i){
mp[i] = lct::newNode(i);
read(e[i].u);read(e[i].v);
if( lct::findroot(lct::mem+e[i].u) != lct::findroot(lct::mem+e[i].v)){
lct::link(lct::mem+e[i].u,mp[i]);
lct::link(lct::mem+e[i].v,mp[i]);
seg::root[i] = seg::insert(seg::root[i-1],0,m,0);
}else if(e[i].u == e[i].v){
seg::root[i] = seg::insert(seg::root[i-1],0,m,m);
}else{
int x = lct::query(lct::mem+e[i].u,lct::mem+e[i].v);
seg::root[i] = seg::insert(seg::root[i-1],0,m,x);
lct::cut(mp[x],lct::mem+e[x].u);
lct::cut(mp[x],lct::mem+e[x].v);
lct::link(mp[i],lct::mem+e[i].u);
lct::link(mp[i],lct::mem+e[i].v);
}
}
int u,v,lastans = 0;
while(q--){
read(u);read(v);
if(type) u ^= lastans,v ^= lastans;
int x = seg::query(seg::root[u-1],seg::root[v],0,m,0,u-1);
printf("%d\n",lastans = (n - x));
}
getchar();getchar();
return 0;
}

bzoj 3514: GERALD07加强版 lct+可持久化线段树的更多相关文章

  1. 【bzoj3514】Codechef MARCH14 GERALD07加强版 LCT+可持久化线段树

    题目描述 N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. 输入 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密.接下来M行,代表图中的每条边 ...

  2. BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT+可持久化线段树

    自己独自想出来并切掉还是很开心的~ Code: #include <bits/stdc++.h> #define N 400005 #define inf 1000000000 #defi ...

  3. BZOJ 3514 GERALD07加强版 (LCT+主席树)

    题目大意:给定n个点m条边无向图,每次询问求当图中有编号为[L,R]的边时,整个图的联通块个数,强制在线 神题!(发现好久以前的题解没有写完诶) 我们要求图中联通块的个数,似乎不可搞啊. 联通块个数= ...

  4. [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)

    [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...

  5. bzoj 3673&3674 可持久化并查集&加强版(可持久化线段树+启发式合并)

    CCZ在2015年8月25日也就是初三暑假要结束的时候就已经能切这种题了%%% 学习了另一种启发式合并的方法,按秩合并,也就是按树的深度合并,实际上是和按树的大小一个道理,但是感觉(至少在这题上)更好 ...

  6. 【XSY2528】道路建设 LCT 可持久化线段树

    题目描述 给你一个\(n\)个点\(m\)条边图,\(q\)个询问,每次问你边权在\([l,r]\)之间的边组成的最小生成树(森林)的边权和.强制在线. \(n,m,q\leq 100000\) 题解 ...

  7. BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)

    BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...

  8. BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)

    首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...

  9. bzoj 4504: K个串 可持久化线段树+堆

    题目: Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一 个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想 ...

随机推荐

  1. PopupWindowFromBottom 从底部弹出popupwindow

    自定义PopupWindowFromBottom public class PopupWindowFromBottom extends PopupWindow { public PopupWindow ...

  2. RMQ with Shifts(线段树)

    RMQ with Shifts Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u Pra ...

  3. js apply / call 函数

    这两个函数的作用是: 将函数绑定到另外一个对象上去运行 用call和apply应用另一个函数(类)以后,当前的函数(类)就具备了另一个函数(类)的方法或是属性,这也能够称之为“继承”. functio ...

  4. 14.Django自带的admin配置

    admin有自己的默认显示,要自定义显示的样式,一般需要自己定义一个类,在自己定义的类里进行相应的设置,然后,把自己的类交给装饰器 交给装饰器的方法有两种: 1.@admin.register(Pub ...

  5. NeurIPS2018: DropBlock: A regularization method for convolutional networks

    NIPS 改名了!改成了neurips了... 深度神经网络在过参数化和使用大量噪声和正则化(如权重衰减和 dropout)进行训练时往往性能很好.dropout 广泛用于全连接层的正则化,但它对卷积 ...

  6. cloudera impala编译 安装 配置 启动

    无论是采用GDB调试impala或者尝试修改impala源码,前提都是需要本地环境编译impala,这篇文章详细的分享一下impala编译方法以及编译过程遇到的棘手的问题: 前言: impala官方的 ...

  7. iOS项目中获取验证码倒计时及闪烁问题解决方案

    -(void)startTime{ __block int timeout= 59; //倒计时时间 dispatch_queue_t queue = dispatch_get_global_queu ...

  8. 【leetcode刷题笔记】Decode Ways

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  9. 316python 基础之计算机基础、Python简介、变量、注释、基础数据类型初识、if、while、语句

    一.计算机基础. cpu:相当于人的大脑,运算与控制中心. 速度 飞机 内存:4G,8G,16G....暂时存储,供给cpu数据. 速度 高铁.成本高,断电即消失. 硬盘:相当于你电脑的数据库,存储着 ...

  10. 20145229吴姗珊 《Java程序设计》小总结

    20145229吴姗珊 <Java程序设计>小总结 教材学习内容总结 由于今天考试考到了操作题,根本无从下手,然后才意识到原来之前的学习都是蜻蜓点水,一味的把学习建立在给老师学,为家长学的 ...