【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)
http://www.lydsy.com/JudgeOnline/problem.php?id=1146
第一种做法(时间太感人):
第二种做法(rank5,好开心)
================================8-20===============================
这题我真的逗了,调了一下午,疯狂造数据,始终找不到错。
后来发现自己sb了,更新那里没有打id,直接套上u了。我。。。。
调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗
好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种。
树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要加上它重叠的数量,这点自己调试的时候找出来了)
我们将路径放进一个池子里,然后累计排名就行了。
时间很感人啊。
(希望以后不要犯这种sb错了,唉,太逗。)
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define dbg(x) cout << #x << "=" << x << endl
#define read(x) x=getint()
#define rdm(u) for(int i=ihead[u]; i; i=e[i].next)
#define lc x<<1
#define rc x<<1|1
#define lson l, m, lc
#define rson m+1, r, rc
#define MID (l+r)>>1
inline const int getint() { char c=getchar(); int ret=0, k=1; for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) ret=ret*10+c-'0'; return k*ret; }
const int N=80010, oo=~0u>>1;
int n, q, ihead[N], brr[N], arr[N], cnt, bak, flg, L, R, same;
int fa[N], top[N], son[N], dep[N], sz[N], id[N], tot, num;
struct Ed { int to, next; }e[N<<1];
inline void add(const int &u, const int &v) {
e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v;
e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u;
} struct node* null;
struct node {
node* ch[2]; int wei, key, sz, cnt;
void pushup() { sz=ch[0]->sz+ch[1]->sz+cnt; }
node(int _w=0, int _sz=1, int _cnt=1) : key(_w), sz(_sz), cnt(_cnt) {
ch[0]=ch[1]=null; wei=rand();
}
}*root[N*50], *nd[N*50];
inline void rot(node* &x, const bool d) {
node* t=x->ch[!d]; x->ch[!d]=t->ch[d]; t->ch[d]=x;
x->pushup(); t->pushup();
x=t;
}
void insert(node* &x, const int &key) {
if(x==null) { x=new node(key); return; }
if(key==x->key) { ++x->cnt; ++x->sz; return; }
bool d=key>x->key;
insert(x->ch[d], key);
if(x->wei>x->ch[d]->wei) rot(x, !d);
x->pushup();
}
void remove(node* &x, const int &key) {
if(x==null) return;
bool d=key>x->key;
if(key==x->key) {
if(x->cnt>1) { --x->cnt; --x->sz; return; }
d=x->ch[0]->wei > x->ch[1]->wei;
if(x->ch[d]==null) {
delete x;
x=null;
return;
}
rot(x, !d);
remove(x->ch[!d], key);
}
else remove(x->ch[d], key);
x->pushup();
}
inline int rank(node* x, const int &key) {
int ret=0, s;
while(x!=null) {
s=x->ch[1]->sz + x->cnt;
if(key==x->key) same+=x->cnt;
if(key<x->key) ret+=s, x=x->ch[0];
else x=x->ch[1];
}
return ret;
}
void build(const int &l, const int &r, const int &x) {
if(l==r) { root[x]=new node(brr[l]); return; }
int m=MID;
build(lson); build(rson);
root[x]=new node(brr[l]);
for(int i=l+1; i<=r; ++i) insert(root[x], brr[i]);
}
void update(const int &l, const int &r, const int &x) {
if(l==r) {
remove(root[x], bak);
insert(root[x], flg);
return;
}
int m=MID;
remove(root[x], bak);
insert(root[x], flg);
if(L<=m) update(lson); if(m<R) update(rson);
}
void query(const int &l, const int &r, const int &x) {
if(L<=l && r<=R) { nd[++num]=root[x]; return; }
int m=MID;
if(L<=m) query(lson);
if(m<R) query(rson);
}
void dfs1(const int &u) {
sz[u]=1; int v;
rdm(u) if(fa[u]!=(v=e[i].to)) {
fa[v]=u; dep[v]=dep[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(const int &u, const int &tp) {
id[u]=++tot; top[u]=tp; brr[tot]=arr[u];
if(son[u]) dfs2(son[u], tp);
rdm(u) if(fa[u]!=e[i].to && son[u]!=e[i].to) dfs2(e[i].to, e[i].to);
}
void getrange(int x, int y) {
num=0;
int fx=top[x], fy=top[y];
while(fx!=fy) {
if(dep[fx]<dep[fy]) { swap(x, y); swap(fx, fy); }
L=id[fx], R=id[x];
query(1, n, 1);
x=fa[fx]; fx=top[x];
}
if(dep[x]>dep[y]) swap(x, y);
L=id[x], R=id[y];
query(1, n, 1);
}
int getrank(const int &key) {
int ret=0; same=0;
for(int i=1; i<=num; ++i) ret+=rank(nd[i], key);
return ret;
}
int getans(int x, int y, int k) {
getrange(x, y);
node* rt; int l=oo+1, r=oo, s=0;
for(int i=1; i<=num; ++i) s+=nd[i]->sz;
if(s<k) return l;
for(int i=1; i<=num; ++i) {
rt=nd[i];
while(rt!=null) {
if(rt->key<l) { rt=rt->ch[1]; continue; }
if(rt->key>r) { rt=rt->ch[0]; continue; }
s=getrank(rt->key);
if(s+1<=k && k<=s+same) return rt->key; //这里要注意
if(s+same>k) { l=rt->key; rt=rt->ch[1]; }
else { r=rt->key; rt=rt->ch[0]; }
}
}
return l;
}
int main() {
null=new node(0, 0, 0); null->wei=oo;
read(n); read(q);
int u, v, k, ans;
for(int i=1; i<=n; ++i) read(arr[i]);
for(int i=1; i<n; ++i) { read(u); read(v); add(u, v); }
dfs1(1); dfs2(1, 1); build(1, n, 1);
while(q--) {
read(k); read(u); read(v);
if(!k) {
bak=arr[u]; flg=arr[u]=v;
L=R=id[u];
update(1, n, 1);
}
else {
ans=getans(u, v, k);
if(ans==oo+1) puts("invalid request!");
else printf("%d\n", ans);
}
}
return 0;
}
===============================8-22============================
学习了主席树和dfs序还有lca后,终于来a这题了。
很爽。真的很爽。
代码简短,而且飞快,排到了rank5,而且这次写程序+调试只用了1小时多!!!!!!
巨大的进步,加油!
说说怎么做吧。
一开始看到dfs序很神奇的,其实很简单。
我们可以知道,从某节点x遍历下去的子树,他们的顺序一定是连续的(因为是dfs)
所以我们可以得知,如果用树状数组来维护的话,那么就要改变它所有的子树。
假设没有修改操作,那么返回这题了http://www.cnblogs.com/iwtwiioi/p/3929098.html
恩,我们来想怎么解决这个修改问题。
首先我们知道,主席树是根据前缀思想维护1-x的一种线段树,我们用上边那题(下边均称为COT)的做法,在每个节点都建到根的主席树。
那么假设无修改,那么询问u到v的路径第k大的答案就在 u到根的主席树 + v到根的主席树 - lca(u, v)到根的主席树 - fa[lca(u, v)]到根的主席树 之中。
前缀区间用啥维护呢?恩!树状数组。
但是我们并不改变节点上的主席树的形态,并不在上边操作,也就是说,建好这些主席树后,就不操作他们了。
我们再开数组用来当树状数组,因为n个节点,自然我们维护1-n的信息,但是我们知道,dfs序在不同子树顺序当然是不同的。而我们需要做到在我们需要的区间内(一段连续的子树的dfs序)做操作。
我们来想想普通的树状数组吧。
操作都是 区间1-x (求和操作) 区间x-n (更新操作)
解决方案就是,再用前缀和思想,如果要求和 [l, r] 那么我们先求和 [1, r] 再减去 求和[1, l-1]
同理,更新的话,先更新[l, n] 再减去更新(即加上相反数) [r+1, n]
哈哈,问题得到解决。
- 咱们用tarjan离线求出lca,并且求出dfs序,并且构造每个点的主席树
- 咱们dfs出来的序在一定子树是连续的。
- 咱们节点x改变,起相应的子树的树状数组也要改变
- 咱们不在原来每个点的主席树操作
- 咱们只在树状数组进行操作
- 咱们求k大时将原来点的主席树和树状数组放到池子里,然后加加减减
问题完美解决!
需要注意:
- 题目是求k大
- 主席树开的空间很大,尽量开
- 一些数组也要开到n的几倍
自己好好领悟!
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define dbg(x) cout << #x << " = " << x << endl
#define rep(i, n) for(int i=0; i<n; ++i)
#define for1(i, a, n) for(int i=a; i<=(n); ++i)
#define read(x) x=getint()
#define MID (l+r)>>1
#define rdm(u) for(int i=ihead[u]; i; i=e[i].next)
using namespace std;
inline const int getint() { char c=getchar(); int k=1, r=0; for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
const int N=80005;
struct ED { int to, next; } e[N<<1];
struct ND { int l, r, s; } t[N*80];
int ihead[N], root[N], c[N], cnt, tot, num, suml, sumr, n, m, lca[N], L[N<<2], R[N<<2], FF[N], LL[N], fa[N], p[N], a[N], ans[N+N], u[N], v[N], K[N];
bool vis[N];
vector<pair<int, int> > q[N];
inline void add(const int &u, const int &v) {
e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v;
e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u;
}
void update(const int &l, const int &r, int &pos, const int &key, const int &sz) {
t[++tot]=t[pos]; pos=tot; t[pos].s+=sz;
if(l==r) return;
int m=MID;
if(key<=m) update(l, m, t[pos].l, key, sz); else update(m+1, r, t[pos].r, key, sz);
}
int query(const int &l, const int &r, const int &k) {
if(l==r) return l;
int s=0, tt=0, m=MID;
for1(i, 1, sumr) s+=t[t[R[i]].r].s, tt+=t[R[i]].s; //将池子里要加减的求和
for1(i, 1, suml) s-=t[t[L[i]].r].s, tt-=t[L[i]].s;
if(tt<k) return -1; //当数目不够时,自己可以算算
if(k<=s) { //这里是求k大,不是k小!!!
for1(i, 1, suml) L[i]=t[L[i]].r;
for1(i, 1, sumr) R[i]=t[R[i]].r;
return query(m+1, r, k);
}
else {
for1(i, 1, suml) L[i]=t[L[i]].l;
for1(i, 1, sumr) R[i]=t[R[i]].l;
return query(l, m, k-s);
}
}
inline void get(int x, const int &k) {
if(k) { R[++sumr]=root[x]; for(x=FF[x]; x; x-=(x&-x)) R[++sumr]=c[x]; } //这里要注意,要加上原本信息,即root
else { L[++suml]=root[x]; for(x=FF[x]; x; x-=(x&-x)) L[++suml]=c[x]; }
}
inline void change(int x, const int &key, const int& k) {
for(; x<=n; x+=(x&-x)) update(1, num, c[x], key, k);
}
int ifind(const int &x) { return x==p[x]?x:p[x]=ifind(p[x]); }
void dfs(const int &x) {
p[x]=x; root[x]=root[fa[x]]; update(1, num, root[x], a[x], 1);
static int nm=0;
FF[x]=++nm; //首次发现x的序
rdm(x) if(fa[x]!=e[i].to) { fa[e[i].to]=x; dfs(e[i].to); p[e[i].to]=x; }
LL[x]=nm; //遍历完x的所有子树后的序,那么这些子树的连续区间就是[FF[x], LL[x]]
vis[x]=1;
int t=q[x].size();
rep(i, t) if(vis[q[x][i].first]) lca[q[x][i].second]=ifind(q[x][i].first);
} int main() {
read(n); read(m); int tt=0;
for1(i, 1, n) { read(a[i]); ans[++tt]=a[i]; }
rep(i, n-1) add(getint(), getint());
for1(i, 1, m) {
read(K[i]); read(u[i]); read(v[i]);
if(K[i]) {
q[u[i]].push_back(pair<int, int> (v[i], i));
q[v[i]].push_back(pair<int, int> (u[i], i));
}
else ans[++tt]=v[i];
}
sort(ans+1, ans+1+tt); ans[tt+1]=1000000013; //离散
for1(i, 1, tt) if(ans[i]!=ans[i+1]) ans[++num]=ans[i]; //将重叠的合并,缩短主席树的区间范围
for1(i, 1, n) a[i]=lower_bound(ans+1, ans+1+num, a[i])-ans; //查找原来元素在ans域的位置,即主席树上对应的区间值
dfs((n+1)>>1); //这里我们从中间遍历,防止爆栈(至于为什么,你自己画一条长链,你懂的)
int out, x, y;
for1(i, 1, m) {
if(K[i]) {
suml=sumr=0;
get(u[i], 1); get(v[i], 1); //将u到根、v到根的原本信息(root)和修改信息(c)全部放到池子里
get(lca[i], 0); get(fa[lca[i]], 0); //同上
out=query(1, num, K[i]);
if(out<=0) puts("invalid request!");
else printf("%d\n", ans[out]);
}
else {
x=u[i]; y=v[i];
y=lower_bound(ans+1, ans+1+num, y)-ans; //求出修改的值在ans的位置
change(FF[x], a[x], -1); change(LL[x]+1, a[x], 1); //首先将区间[FF[x], n]全部减去a[x],然后将区间[LL[x]+1, n]全部加回a[x]
change(FF[x], y, 1); change(LL[x]+1, y, -1); //同上
a[x]=y;
}
}
return 0;
}
Description
M 公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通 信网络。该网络的结构由N个路由器和N-1条高速光缆组成。每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信 子网与其他部门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行数据交换会带来很大的延迟。而两个路由器 之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来 监视公司的网络状况。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通信路径上延迟第k大的路由器 的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这 Q条询问信息,它们可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。
Input
第 一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由器初始的数据延迟时间Ti。紧接着N-1行,每行 包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的 数据交换延迟时间由Ta变为b。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意a可 以等于b,此时路径上只有一个路由器。
Output
对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。
Sample Input
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5
Sample Output
2
2
invalid request!
HINT
Source
【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)的更多相关文章
- BZOJ 1146: [CTSC2008]网络管理Network 树链剖分+线段树+平衡树
1146: [CTSC2008]网络管理Network Time Limit: 50 Sec Memory Limit: 162 MBSubmit: 870 Solved: 299[Submit] ...
- BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )
树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT+(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. ...
- BZOJ 1146: [CTSC2008]网络管理Network [树上带修改主席树]
1146: [CTSC2008]网络管理Network Time Limit: 50 Sec Memory Limit: 162 MBSubmit: 3522 Solved: 1041[Submi ...
- BZOJ 1146: [CTSC2008]网络管理Network 带修改主席树_树套树_DFS序
Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路 ...
- [BZOJ 1146] [CTSC2008]网络管理Network(树状数组+主席树)
题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高 ...
- bzoj 1146 [CTSC2008]网络管理Network
很久之前写过 count on the tree. 然后一直不懂树状数组是怎么套上这个主席树的. 看了两小时发现它套的就是个权值线段树, 看不出来可持久化在哪里. 因为动态开点所以空间nlog2n. ...
- [BZOJ1146][CTSC2008]网络管理Network(二分+树链剖分+线段树套平衡树)
题意:树上单点修改,询问链上k大值. 思路: 1.DFS序+树状数组套主席树 首先按照套路,关于k大值的问题,肯定要上主席树,每个点维护一棵权值线段树记录它到根的信息. 关于询问,就是Que(u)+Q ...
- HDU 2460 Network(双连通+树链剖分+线段树)
HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...
- BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )
BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 ) 题意分析 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 ...
随机推荐
- Linux 常用软件和服务的配置网站
http://www.linuxidc.com/Linux/2012-11/73862.htm Linux zabbix的安装和配置
- 【OpenStack】OpenStack系列10之Horizon详解
一.参考其他资料即可.可以采用haproxy+apache+horizon方式部署,haproxy/httpd支持ssl.
- 【JAVA、C++】LeetCode 002 Add Two Numbers
You are given two linked lists representing two non-negative numbers. The digits are stored in rever ...
- 1.python基础入门
作者:刘耀 出处:http://www.yaomr.com 欢迎转载 提示: 语法基于python3.5版本(会提示2.7版本和3.5版本的区别) Python命令行将以>>>开始, ...
- window.open()读取本地图片简单使用总结
最近做了一个项目,需要读取本地图片出来,问了一些人,感觉在数据库中存取路径比较合适,故做此方法. 后台查询出来的路径
- NEFU 1151 特殊的质数
特殊的质数 Problem:1151 Time Limit:1000ms Memory Limit:65535K Description 如果一个数是质数,而且他在质数表中的编号也是质数,称为特殊的质 ...
- CodeIgniter - 集成七牛云存储
最近有一个项目需要集成七牛云存储的图片存储和调用功能,程序是基于CodeIgniter2.1.3的PHP框架.刚拿到手完全无从下手的感觉,因为像框架这种东西,想从官方的PHPSDK集成进去,需要改动很 ...
- device unauthorized & ANDROID_ADB_SERVER_PORT 问题解决
最近做安卓开发的时候,碰到了如下两个极品问题,刚开始晕头转向,仔细一研究终于解决了: FAQ1: C:\Users\xxx>adb shelladb: Env var ANDROID_ADB_S ...
- onSaveInstanceState和onRestoreInstanceState
本文摘自: http://h529820165.iteye.com/blog/1399023 Android calls onSaveInstanceState() before the activi ...
- CC2540开发板学习笔记(四)——定时器
一.实验内容 分别使用定时器T1和T3使得LED周期性闪烁 二.实验过程 1.定时器T1(查询IRCON来控制) (1)需要调配的寄存器 T1CTL(0XE4) Timer1控制寄存器 BIT3, ...