【BZOJ1146】【树链剖分+平衡树】网络管理Network

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 55 1 2 3 43 12 14 35 32 4 50 1 22 2 32 1 43 3 5

Sample Output

322invalid request!

Hint

任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N 。

Source

CTSC2008

【分析】

居然1A了,天不负有心人啊.....写了1个半小时TAT

裸的树上带修改第k大,用树链剖分转成线段树,在每个线段树里面维护一个平衡树。

查找的时候先二分答案,再按照树链剖分的方式找就行了。

发现动态内存分配还是蛮好的,均摊分析,空间复杂度不会爆。

  1 /*
  2 唐代韦庄
  3 《菩萨蛮·劝君今夜须沈醉》
  4 劝君今夜须沉醉,尊前莫话明朝事。珍重主人心,酒深情亦深。
  5 须愁春漏短,莫诉金杯满。遇酒且呵呵,人生能几何!
  6 */
  7 #include <iostream>
  8 #include <cstdio>
  9 #include <algorithm>
 10 #include <cstring>
 11 #include <vector>
 12 #include <utility>
 13 #include <iomanip>
 14 #include <string>
 15 #include <cmath>
 16 #include <queue>
 17 #include <assert.h>
 18 #include <map>
 19 #include <ctime>
 20 #include <cstdlib>
 21 #include <stack>
 22 #include <set>
 23 #define LOCAL
 24 const int INF = 0x7fffffff;
 25 const int MAXN = 80000  + 10;
 26 const int maxnode = 20000 * 2 + 200000 * 20;
 27 const int MAXM = 1000000 + 10;
 28 const int MAX = 100000000;
 29 using namespace std;
 30 struct Treap{
 31        int val;
 32        int fix, siz;
 33        Treap *ch[2];
 34
 35        void update(){
 36             siz = 1;
 37             if (ch[0] != NULL) siz += ch[0]->siz;
 38             if (ch[1] != NULL) siz += ch[1]->siz;
 39        }
 40 };
 41 //将t的d儿子换到t
 42 void rotate(Treap *&t, int d){
 43      Treap *p = t->ch[d];
 44      t->ch[d] = p->ch[d ^ 1];
 45      t->update();
 46      p->ch[d ^ 1] = t;
 47      p->update();
 48      t = p;
 49      return;
 50 }
 51 Treap *NEW(int val){
 52       Treap *t = new Treap;
 53       t->fix = rand();
 54       t->val = val;
 55       t->siz = 1;
 56       t->ch[0] = t->ch[1] = NULL;
 57       return t;
 58 }
 59 void insert(Treap *&t, int val){
 60      if (t == NULL){
 61         t = NEW(val);
 62         return;
 63      }
 64      int d = (val >= t->val);
 65      insert(t->ch[d], val);
 66      if (t->ch[d]->fix > t->fix) rotate(t, d);
 67      t->update();
 68 }
 69 int size(Treap *&t) {return (t == NULL) ? 0: t->siz;}
 70 //统计比val大的数量,包括val!
 71 int get(Treap *&t, int val){
 72     if (t == NULL) return 0;
 73     //右边都比他大..
 74     if (val <= t->val) return size(t->ch[1]) + 1 + get(t->ch[0], val);
 75     else return get(t->ch[1], val);
 76 }
 77 void erase(Treap *&t, int x){
 78      int d;
 79      if (x == t->val) d = -1;
 80      else d = (x > t->val);//随意删除一个就行了
 81
 82      if (d == -1){
 83         Treap *tmp = t;
 84         if (t->ch[0] == NULL){//左儿子为0那么就变成右儿子
 85            t = t->ch[1];
 86            delete tmp;
 87         }else if (t->ch[1] == NULL){
 88            t = t->ch[0];
 89            delete tmp;
 90         }else{//还没到叶子转下去
 91            int f = (t->ch[1]->fix > t->ch[0]->fix);
 92            rotate(t, f);
 93            erase(t->ch[f ^ 1], x);//注意转到另一边了
 94         }
 95      }else erase(t->ch[d], x);
 96      if (t != NULL) t->update();
 97 }
 98
 99 //------------------------以上为Treap------------------//
100
101 struct Node{//线段树的节点
102        int l, r;
103        Treap *t;//注意这个里面用动态内存分配...无奈.
104        Node *ch[2];
105 }*root, mem[MAXN * 2];
106 int n, q;//点数和询问总述数量
107 int head[MAXN * 2], to[MAXN * 2];//vector会爆?
108 int M, next[MAXN * 2], fa[MAXN];
109 int Time, data[MAXN], son[MAXN], siz[MAXN], pos[MAXN];//时间轴
110 int top[MAXN], tot, dep[MAXN];
111
112 void addEdge(int u, int v){
113      to[M] = v;
114      next[M]= head[u];
115      head[u] = M++;
116 }
117 void dfs_1(int u){
118      son[u] = 0;
119      siz[u] = 1;
120      for (int i = head[u];i != -1; i = next[i]){
121          int v = to[i];
122          if (v == fa[u]) continue;
123          fa[v] = u;
124          dep[v] = dep[u] + 1;
125          dfs_1(v);
126          siz[u] += siz[v];
127          if (siz[v] > siz[son[u]]) son[u] = v;
128      }
129 }
130 void dfs_2(int u, int top_node){
131      pos[u] = ++Time;
132      top[u] = top_node;
133      if (son[u]) dfs_2(son[u], top_node);
134      for (int i = head[u]; i != -1; i = next[i]){
135          int v = to[i];
136          if (v == fa[u] || v == son[u]) continue;
137          dfs_2(v, v);
138      }
139 }
140 Node *NEW(int l, int r){
141      Node *p = &mem[tot++];
142      p->l = l;
143      p->r = r;
144      p->t = NULL;//p里面的treap
145      p->ch[0] = p->ch[1] = NULL;
146      return p;
147 }
148 void build(Node *&t, int l, int r){
149      if (t == NULL){
150         t = NEW(l, r);
151      }
152      if (l == r) return;
153      int mid = (l + r) >> 1;
154      build(t->ch[0], l, mid);
155      build(t->ch[1], mid + 1, r);
156 }
157 //居然是单点修改OAO
158 //将l位置的路由器延迟时间由x变为y
159 void change(Node *&p, int l, int x, int y){
160      if (p->l == l && p->r == l){
161         if (x != INF) erase(p->t, x);//为INF的时候就是单纯的插入
162         insert(p->t, y);
163         return;
164      }
165      if (x != INF) erase(p->t, x);
166      insert(p->t, y);
167
168      int mid = (p->l + p->r) >> 1;
169      if (l <= mid) change(p->ch[0], l, x, y);
170      else change(p->ch[1], l, x, y);
171 }
172 //表示在l,r这一段内比val大的数的个数
173 int query(Node *&p, int l, int r, int val){
174     if (l <= p->l && p->r <= r) return get(p->t, val);
175     int mid = (p->l + p->r) >> 1;
176     int sum = 0;
177     if (l <= mid) sum += query(p->ch[0], l, r, val);
178     if (r > mid) sum += query(p->ch[1], l, r, val);
179     return sum;
180 }
181 void init(){
182      memset(head, -1, sizeof(head));
183      memset(dep, 0, sizeof(dep));//表示根到自己不包含根的节点数量
184      siz[0] = fa[1] = 0;
185      M = Time = 0;
186
187      scanf("%d%d", &n, &q);
188      for (int i = 1; i <= n; i++) scanf("%d", &data[i]);
189      for (int i = 1; i < n; i++){
190          int u, v;
191          scanf("%d%d", &u, &v);
192          addEdge(u, v);
193          addEdge(v, u);
194      }
195      dfs_1(1);
196      dfs_2(1, 1);
197      build(root, 1, Time);
198      for (int i = 1; i <= n; i++)
199      change(root, pos[i], INF, data[i]);
200 }
201 //返回在lr区间内比x大的数个数
202 int check(int l, int r, int x){
203     int sum = 0;
204     while (top[l] != top[r]){
205           //保证永远矮的往上爬
206           if (dep[top[l]] < dep[top[r]]) swap(l, r);
207           sum += query(root, pos[top[l]], pos[l], x);
208           l = fa[top[l]];
209     }
210     //显然l应该高一点
211     if (dep[l] > dep[r]) swap(l, r);
212     sum += query(root, pos[l], pos[r], x);
213     return sum;
214 }
215 int query(int L, int R, int k){
216     //首先二分最大值k
217     int Ans, l = -MAX, r = MAX;
218     while (l <= r){
219           int mid = (l + r) >> 1;
220           if (mid == 3)
221           printf("");
222           if (check(L, R, mid) >= k) Ans = mid, l = mid + 1;
223           else r = mid - 1;
224     }
225     //printf("%d", check(L, R, 3));
226     return Ans;
227 }
228 int search(int l, int r){
229     int sum = 0;
230     while (top[l] != top[r]){
231           if (dep[top[l]] < dep[top[r]]) swap(l, r);
232           sum += dep[l] - dep[top[l]] + 1;
233           l = fa[top[l]];
234     }
235     //dep[r]大
236     if (dep[l] > dep[r]) swap(l, r);
237     sum += dep[r] - dep[l] + 1;
238     return sum;
239 }
240 void work(){
241      for (int i = 1; i <= q; i++){
242
243          int l, r, k;
244          scanf("%d%d%d", &k, &l, &r);
245          //if (i == 5)
246          //printf("%d", k);
247          if (k == 0){//修改操作
248             change(root, pos[l], data[l], r);
249             data[l] = r;
250          }else{//查询
251             int num = search(l, r);//查询l到r之间的节点个数
252             if (k > num) {printf("invalid request!\n");continue;}
253             printf("%d\n", query(l, r, k));
254          }
255      }
256 }
257 void debug(){
258      /*Treap *root = NULL;
259      insert(root, 2);
260      printf("%d", get(root, 2));*/
261      //printf("%d", search(4, 2));
262      //for (int i = 1; i <= 5; i++) printf("%d\n", pos[i]);
263      //printf("%d", query(root, 2, 3, 3));
264 }
265
266 int main(){
267     //srand(time(0));
268
269     init();
270     work();
271     //debug();
272     return 0;
273 }

时间: 2024-08-25 13:41:57

【BZOJ1146】【树链剖分+平衡树】网络管理Network的相关文章

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

BZOJ 1146 CTSC 2008 网络管理 Network 树链剖分+二分答案+平衡树

题目大意:有n个路由器,他们由n-1条边连接(形成一棵树).每一个路由器有一个延时值.有两种操作: 1.查询树上x,y两点之间的路径上第k大的权值是多少 2.修改x位置的权值为y 思路:当我大概想到怎么做这个题的时候,所想的时间复杂度已经达到了O(nlog^4n),偷偷的瞄了一眼数据范围...(N,Q<=80000,时限50s,小心翼翼的掏出计算器算了一下:8w * log(8w) ^  4 ≈ 56E,心中这样想着:Treap有常数,链剖常数大,二分不稳定的范围好像不止8w...评测机会不会很

BZOJ1146 [CTSC2008]网络管理Network 树链剖分 主席树 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链上的第k大值 题解 水题. 就是烦了一点. 树链剖分+带修主席树. 带修主席树: BZOJ1901 Zju2112 Dynamic Rankings 主席树 代码 #include <cstring> #include <cstdio> #include <algorithm&g

BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )

树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT+(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. 时间复杂度O(N*log^3(N)) ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<

HDU - 6393 Traffic Network in Numazu(树链剖分+基环树)

http://acm.hdu.edu.cn/showproblem.php?pid=6393 题意 给n个点和n条边的图,有两种操作,一种修改边权,另一种查询u到v的最短路. 分析 n个点和n条边,实际上是一棵树+一个环,如果仅仅是一棵树,那么这题就是树链剖分的模板题了. 对于环来说,可以考虑先把环中一条边提取出来,然后树链剖分,修改时用线段树,单点修改和区间求和. 查询时就考虑三种情况,u走树上到v,从u经过提取出来的边再到v(两种),取最小值. 至于取出环上一条边,用并查集搞搞. #incl

hdu4729 树链剖分+二分

An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 1235    Accepted Submission(s): 257 Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans t

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In