[BZOJ3514] Codechef MARCH14 GERALD07加强版 (CHEF AND GRAPH QUERIES)

题意

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

部分数据强制在线.

\(1\le N,M,K\le200,000\)

题解

有点意思的LCT题.

原题好像不强制在线于是可以回滚莫队+带撤销并查集水过去.

我们考虑暴力: 把 \([l,r]\) 内的所有点依次加入并查集, 每次若成功合并两个联通块则将答案 \(-1\).

在这种情况下, 一条边会在什么情况下对答案作出什么贡献? 显然是当两条边连接的两个点在左边的合法边都连过之后依然不联通的情况下会造成答案减少 \(1\).

考虑快速计算上面的贡献. 容易发现对于某条边 \(e\), 依次将它左侧的边加入图中, 一旦在某个边 \(e'\) 加入后 \(e\) 两边的点已经被联通, 那么继续加下去一定也是联通的. 又因为查询是将一整段区间的边加入图中, 所以一旦 \(e\) 和 \(e'\) 都被查询区间包含, 则 \(e\) 不会对答案作出贡献. 否则一定会对答案造成贡献.

于是只要我们对于所有 \(e\) 都预处理出 \(e'\) 的位置, 我们就可以通过查询 \([l,r]\) 内小于 \(l\) 的值的个数解决. 这个问题显然主席树/分块均可解决.

至于预处理, 我们可以使用LCT. 过程类似于水管局长. 从左到右依次加边, 如果加入边 \((u,v)\) 时出环了, 那么树上从 \(u\) 到 \(v\) 的路径上最早加入的边就是我们要求的. Splay上维护一下代表边的点的位置就好了(边权转点权的普通操作, 加点)

以及好像把点断掉的时候并不用存这个点代表的边是什么...把这个点Splay到根然后把左右子树直接断掉就好了...

以及黄学长&wulala的变量名真的是生动形象

参考代码

#include <bits/stdc++.h>
#define _O0 __attribute__((optimize("O0"))) const int MAXN=200010; struct LCT{
#define lch chd[0]
#define rch chd[1]
#define kch chd[k]
#define xch chd[k^1]
struct Node{
int val;
bool rev;
Node* prt;
Node* min;
Node* chd[2];
Node(int val):val(val),rev(false),prt(NULL),min(this),chd{NULL,NULL}{}
inline bool isRoot(){
return this->prt==NULL||(this->prt->lch!=this&&this->prt->rch!=this);
}
inline _O0 void Flip(){
if(this!=NULL){
std::swap(this->lch,this->rch);
this->rev=!this->rev;
}
}
inline void PushDown(){
if(this->rev){
this->lch->Flip();
this->rch->Flip();
this->rev=false;
}
}
inline void Maintain(){
this->min=this;
if(this->lch&&this->lch->min->val<this->min->val)
this->min=this->lch->min;
if(this->rch&&this->rch->min->val<this->min->val)
this->min=this->rch->min;
}
};
std::vector<Node*> N;
LCT(int n):N(n+1){
for(int i=1;i<=n;i++)
N[i]=new Node(INT_MAX);
}
void Rotate(Node* root,int k){
Node* tmp=root->xch;
root->PushDown();
tmp->PushDown();
tmp->prt=root->prt;
if(!root->isRoot()){
if(root->prt->lch==root)
root->prt->lch=tmp;
else
root->prt->rch=tmp;
}
root->xch=tmp->kch;
if(root->xch)
root->xch->prt=root;
tmp->kch=root;
root->prt=tmp;
root->Maintain();
tmp->Maintain();
} void Splay(Node* root){
while(!root->isRoot()){
int k=root->prt->lch==root;
if(root->prt->isRoot())
Rotate(root->prt,k);
else{
int d=root->prt->prt->lch==root->prt;
Rotate(k==d?root->prt->prt:root->prt,k);
Rotate(root->prt,d);
}
}
} void Expose(Node* root){
Splay(root);
root->PushDown();
if(root->rch){
root->rch=NULL;
root->Maintain();
}
} void Access(Node* root){
Expose(root);
Splay(root);
while(root->prt){
Splay(root->prt);
root->prt->PushDown();
root->prt->rch=root;
root->prt->Maintain();
Splay(root);
}
} void Evert(Node* root){
Access(root);
root->Flip();
} Node* FindRoot(Node* root){
Access(root);
Node* ans=root;
ans->PushDown();
while(ans->lch){
ans->PushDown();
ans=ans->lch;
}
Splay(ans);
return ans;
} void Link(Node* a,Node* b){
Evert(b);
b->prt=a;
} void Cut(Node* a,Node* b){
Evert(a);
Access(b);
b->PushDown();
b->lch->prt=NULL;
b->lch=NULL;
b->Maintain();
} int AddEdge(int a,int b,int val){
if(a==b)
return val;
int ret=0;
if(FindRoot(N[a])==FindRoot(N[b])){
Evert(N[a]);
Access(N[b]);
Node* min=N[b]->min;
ret=N[b]->min->val;
Splay(min);
min->lch->prt=NULL;
min->rch->prt=NULL;
}
N.push_back(new Node(val));
Link(*N.rbegin(),N[a]);
Link(*N.rbegin(),N[b]);
return ret;
}
#undef lch
#undef rch
#undef kch
#undef xch
}; struct SegTree{
struct Node{
int l;
int r;
int sum;
Node* lch;
Node* rch;
Node(int l,int r):l(l),r(r),sum(0),lch(NULL),rch(NULL){
if(l!=r){
int mid=(l+r)>>1;
this->lch=new Node(l,mid);
this->rch=new Node(mid+1,r);
}
}
Node(Node* ptr){
*this=*ptr;
}
void Insert(int x){
++this->sum;
if(l!=r){
if(x<=this->lch->r)
(this->lch=new Node(this->lch))->Insert(x);
else
(this->rch=new Node(this->rch))->Insert(x);
}
}
};
std::vector<Node*> N;
SegTree(int n){
N.push_back(new Node(0,n));
}
void Insert(int x){
N.push_back(new Node(*N.rbegin()));
(*N.rbegin())->Insert(x);
}
int Query(int l,int r,int x){
return Query(N[l-1],N[r],x);
}
int Query(Node* aux,Node* root,int x){
if(0<=root->l&&root->r<=x)
return root->sum-aux->sum;
else{
int ans=0;
if(0<=root->lch->r)
ans+=Query(aux->lch,root->lch,x);
if(root->rch->l<=x)
ans+=Query(aux->rch,root->rch,x);
return ans;
}
}
}; int n,m,q,opt;
int lastans;
int ntr[MAXN]; int main(){
scanf("%d%d%d%d",&n,&m,&q,&opt);
LCT* lct=new LCT(n);
SegTree* T=new SegTree(m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
ntr[i]=lct->AddEdge(a,b,i);
}
for(int i=1;i<=m;i++)
T->Insert(ntr[i]);
for(int i=0;i<q;i++){
int l,r;
scanf("%d%d",&l,&r);
l^=opt*lastans;
r^=opt*lastans;
printf("%d\n",lastans=n-T->Query(l,r,l-1));
}
return 0;
}

[BZOJ 3514]Codechef MARCH14 GERALD07加强版 (CHEF AND GRAPH QUERIES)的更多相关文章

  1. BZOJ 3514: Codechef MARCH14 GERALD07加强版( LCT + 主席树 )

    从左到右加边, 假如+的边e形成环, 那么记下这个环上最早加入的边_e, 当且仅当询问区间的左端点> _e加入的时间, e对答案有贡献(脑补一下). 然后一开始是N个连通块, 假如有x条边有贡献 ...

  2. BZOJ 3514: Codechef MARCH14 GERALD07加强版 [LCT 主席树 kruskal]

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1312  Solved: 501 ...

  3. BZOJ 3514 Codechef MARCH14 GERALD07加强版

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3514 题意:给出一个图m条边.每次询问只加入编号在区间[L,R]之内的边有多少连通 ...

  4. BZOJ 3514: Codechef MARCH14 GERALD07加强版(LCT + 主席树)

    题意 \(N\) 个点 \(M\) 条边的无向图,询问保留图中编号在 \([l,r]\) 的边的时候图中的联通块个数. \(K\) 次询问强制在线. \(1\le N,M,K \le 200,000\ ...

  5. 【刷题】BZOJ 3514 Codechef MARCH14 GERALD07加强版

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

  6. BZOJ 3514 Codechef MARCH14 GERALD07加强版 Link-Cut-Tree+划分树

    题目大意: 给定n个点m条边的无向图.求问当图中仅仅有[编号在[l,r]区间内]的边存在时图中的联通块个数 强制在线 注意联通块是指联通了就是同一块,不是Tarjan求的那种块 看到这题的那一刻我就想 ...

  7. BZOJ 3514: Codechef MARCH14 GERALD07加强版 (LCT维护最大生成树+主席树)

    题意 给出nnn个点,mmm条边.多次询问,求编号在[l,r][l,r][l,r]内的边形成的联通块的数量,强制在线. 分析 LCTLCTLCT维护动态最大生成树,先将每条边依次加进去,若形成环就断掉 ...

  8. 【BZOJ-3514】Codechef MARCH14 GERALD07加强版 LinkCutTree + 主席树

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1288  Solved: 490 ...

  9. 【LCT+主席树】BZOJ3514 Codechef MARCH14 GERALD07加强版

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2023  Solved: 778 ...

随机推荐

  1. 11 java 线程池 使用实例

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  2. ora-01747:因为表中存在关键字造成的

    ORCLE报错解决(ora-01747:无效的用户.表.列,表.列)  一.ora-01747:无效的用户.表.列,表.列 这个问题出现是因为表中存在关键字造成的,如果想新增数据直接用sql语句,查询 ...

  3. MySQL中使用SHOW PROFILE命令分析性能的用法整理(配合explain效果更好,可以作为优化周期性检查)

    这篇文章主要介绍了MySQL中使用show profile命令分析性能的用法整理,show profiles是数据库性能优化的常用命令,需要的朋友可以参考下   show profile是由Jerem ...

  4. 从0开始整合SSM框架--2.spring整合mybatis

    依赖:<properties> <!-- spring版本号 --> <spring.version>4.1.3.RELEASE</spring.versio ...

  5. Angular2-编写一个简易的组件

    Angular2组件可以这么理解:编写一个类,然后在类的上面用组件装饰器装饰一下,这个类就成组件了. 所以编写组件分两步:1)编写类:2)编写装饰器 1)编写类: export class Simpl ...

  6. TypeScript 乱糟笔记

    数组头上插一个值. var arr: Array<String> = ['a', 'b', 'c'];arr.unshift('d'); object删除元素. var obj: Obje ...

  7. 如鹏网学习笔记(八)CSS

    CSS 一.CSS简介 1,CSS (Cascading Style Sheets) 级联样式表 ,是一种计算机语言,用来控制HTML内容的显示效果 2,CSS预先定义了众多的和显示效果有关的样式属性 ...

  8. [javaSE] 标识符大小写

    java中是严格区分大小写的. PHP中函数,类名称不区分大小写,变量和常量区分大小写 public class VariableDemo { public static void test(){ S ...

  9. 运行javac编译报错:仅当显式请求注释处理时才接受类名称“xxxxxx”

    发生原因:运行javac编译时没有加上扩展名.解决方法:加上.java扩展名重新编译即可,"xxxxxx.java".

  10. java:模拟队列操作

    import java.util.LinkedList; public class Myqueue { private LinkedList<Object> linkedList; pub ...