题目大意:

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. linux下jdk多版本管理

    linux下jdk多版本管理 项目开发中,不管是哪种语言都避免不了多个版本环境管理问题(本文虽然以jdk为例来写的,但不仅限于jdk),如何能做到快速的环境升级与切换确实是一件深思的事! 安装jdk ...

  2. java高级主题

    1 java.util.concurrent.locks.LockSupport park:阻塞线程. unpark:解除阻塞线程. 线程阻塞最基础的组件. 2 sun.misc.Unsafe 可以用 ...

  3. NSURLSession各文件关系

    NSURLSession   通过session创建任务 @property (class, readonly, strong) NSURLSession *sharedSession; + (NSU ...

  4. Modeling of Indoor Positioning Systems Based on Location Fingerprinting

    Kamol Kaemarungsi and Prashant Krishnamurthy Telecommunications Program School of Information Scienc ...

  5. struts2 封装获取表单数据的方式

    一.属性封装 1.在action中设置成员变量,变量名与表单中的name属性值相同 2.生成变量的set方法 实例 获取用户输入的用户名和密码 jsp页面 java代码 二.模型驱动(常用) 1.ac ...

  6. linux c编程:信号(一)

    信号是软件中断,很多比较重要的应用程序都需要处理信号.并且信号提供了一种处理异步事件的方法.如终端用户键入中断键,会通过信号机制停止一个程序,或及早终止管道中的下一个程序 很多条件都可以产生信号,比如 ...

  7. CentOS7.x 报错 There are no enabled repos.

    地址 :http://mirrors.163.com/centos/7/os/x86_64/Packages/   wget http://mirrors.163.com/centos/7/os/x8 ...

  8. bug-2——tab中beforeActivate:在对象活动前触发

    $j("#tabs").tabs({ beforeActivate:function(event,ui){ var ret = apoCheck('${requestScope.a ...

  9. Android中的资源访问

    Android中的资源是指非代码部分,指外部文件. assets中保存的一般是原生的文件,例如MP3文件,Android程序不能直接访问,必须通过AssetManager类以二进制流的形式来读取. r ...

  10. SAP 改表方法

    SAP中直接修改表.视图的Tcode有SE16N和SM30. 1. SE16N修改表需要先输入命令&SAP_EDIT,回车左下角显示激活SAP编辑功能后,就可以对相应的表进行新增.删除.修改的 ...