bzoj 3514: GERALD07加强版 lct+可持久化线段树

时间:2023-03-09 05:57:11
bzoj 3514: GERALD07加强版 lct+可持久化线段树

题目大意:

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;
}