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;
}
bzoj 3514: GERALD07加强版 lct+可持久化线段树的更多相关文章
- 【bzoj3514】Codechef MARCH14 GERALD07加强版 LCT+可持久化线段树
题目描述 N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. 输入 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密.接下来M行,代表图中的每条边 ...
- BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT+可持久化线段树
自己独自想出来并切掉还是很开心的~ Code: #include <bits/stdc++.h> #define N 400005 #define inf 1000000000 #defi ...
- BZOJ 3514 GERALD07加强版 (LCT+主席树)
题目大意:给定n个点m条边无向图,每次询问求当图中有编号为[L,R]的边时,整个图的联通块个数,强制在线 神题!(发现好久以前的题解没有写完诶) 我们要求图中联通块的个数,似乎不可搞啊. 联通块个数= ...
- [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)
[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...
- bzoj 3673&3674 可持久化并查集&加强版(可持久化线段树+启发式合并)
CCZ在2015年8月25日也就是初三暑假要结束的时候就已经能切这种题了%%% 学习了另一种启发式合并的方法,按秩合并,也就是按树的深度合并,实际上是和按树的大小一个道理,但是感觉(至少在这题上)更好 ...
- 【XSY2528】道路建设 LCT 可持久化线段树
题目描述 给你一个\(n\)个点\(m\)条边图,\(q\)个询问,每次问你边权在\([l,r]\)之间的边组成的最小生成树(森林)的边权和.强制在线. \(n,m,q\leq 100000\) 题解 ...
- BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)
BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...
- BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)
首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...
- bzoj 4504: K个串 可持久化线段树+堆
题目: Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一 个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想 ...
随机推荐
- Android 适配(drawable文件夹)图片适配(二)
参考自(https://blog.csdn.net/myoungmeng/article/details/54090891) Android资源文件存放: android的drawable文件一共可以 ...
- 如何在iOS中使用libxml
本文转载至 http://blog.csdn.net/cloudhsu/article/details/8087628 1. 选择xcode工程设定 2. 选择target 3. 选择Summary ...
- gulp安装教程
1.安装nodejs并选装cnpm: npm install cnpm -g --registry=https://registry.npm.taobao.org 2.全局安装gulp: cnpm i ...
- Python高级编程第二版--笔记
不只是CPython Stackless Python Jython(与java集成) IronPython(与net集成) PyPy python真正出众的领域在于围绕语言打造的整个生态系统. Py ...
- MySQL查询含转义字符反斜杠的时候一直为零查询无效
今天在查询一个字段时显示一直显示为0,可是表里明明有对应的数值. ) as vote_your_count from vote_beauty where beauty_person = '\u5c0f ...
- 获取exe文件窗口抓图,将memo转化为JPG输出
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System ...
- jetty源代码剖析
近期使用jetty自己写了一个web server,如今闲了花了一天的时间看了一jetty的源代码,主要以server的启动为主线.进行了剖析,经过阅读对jetty的源代码大赞,写的简洁.清晰.架构也 ...
- https 请求发送 例子 tls && ssl
package com.dooioo.training.helper; import java.io.IOException; import java.io.UnsupportedEncodingEx ...
- HDU - 1598 find the most comfortable road 【最小生成树】
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1598 思路 用kruskal 算法 将边排序后 跑 kruskal 然后依次将最小边删除 再去跑 kr ...
- VC2010编译时提示:转换到 COFF 期间失败: 文件无效或损坏
有时候新安装好VS2010后编译时就提示上述错误,罗列一下从网上查找到的几种解决方案: 方案1: 点击“项目”-->“属性”--> “清单工具”, 然后选择"输入和输出’--&g ...