4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分

GDOI之后写的第一道题。看到之后没什么感觉(是我太弱,中途一度想用kpm之前在某道题上用过的链表的方法。想了想应该不可能。)

好!让我们来分析这道题吧!首先简化模型,它是要求维护树上的一些路径,支持添加和修改,要求不经过某个点的路径的最大权值(不经过某个点,我一度想到了动点分,虽然我还不会)。

我们可以先考虑在链上(其实仔细一想,如果链上的你会做,那么树上的大多数情况下便是多个了链剖而已吧!)的情况。在链上,有一些区间覆盖,要求没有覆盖某个点的区间的最大权值。那么我们接着想如果询问2询问了一个点,那么所有覆盖了这个点的区间都是无效的。换句话说如果A这个区间内含有N个点,那么A这个区间对于这N个点是无效,而对于其他的所有点都是有效的。所以接下来,如果是链上的,一个区间的有效范围便是 1—— 左端点-1, 右端点+1——N。  再把它搬回树上,接下来链剖之后,x -- y 的路径可能有 log2N个区间, 要一个个修改。再乘上线段树的log2N。 复杂度大概是log2N的平方*m。 (另外因为维护最大值所以线段树上的节点都是堆,当然C++的肯定就用优先队列了。)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<queue>
 4 #include<algorithm>
 5 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 6 #define ez(i,j) for(edge*i = head[j]; i; i=i->next)
 7 #define maxn 100233
 8 using namespace std;
 9
10 struct edge{ int to; edge*next; } e[maxn*2], *pt = e, *head[maxn];
11 inline void add(int x,int y) { pt->to = x, pt->next = head[y], head[y] = pt++; pt->to = y, pt->next = head[x], head[x] = pt++; }
12
13 int fa[maxn], dep[maxn], top[maxn], w[maxn], son[maxn], sz[maxn], tot = 0;
14 #define to i->to
15 inline void dfs1(int x) {
16     sz[x] = 1; son[x] = 0;
17     ez(i,x) {
18         if( to == fa[x] ) continue;
19         fa[to] = x; dep[to] = dep[x] + 1; dfs1(to);
20         sz[x] += sz[to]; if( sz[to] > sz[son[x]] ) son[x] = to;
21     }
22 }
23 inline void dfs2(int x,int ph) {
24     top[x] = ph; w[x] = ++tot;
25     if( son[x] ) dfs2(son[x],ph);
26     ez(i,x) if( to != son[x] && to != fa[x] ) dfs2(to,to);
27 }
28
29 inline int read() {
30     int s = 0, t = 1; char c = getchar();
31     while( !isdigit(c) ) { if( c == ‘-‘ ) t = -1; c = getchar(); }
32     while( isdigit(c) ) s = s * 10 + c - 48, c = getchar();
33     return s * t;
34 }
35 struct node{ int l, r; bool operator < (const node&rhs) const { return l < rhs.l; } } nod[maxn];
36 priority_queue<int> a[maxn*4], b[maxn*4];
37 inline int attop(int x) {
38     while( !a[x].empty() && !b[x].empty() ) {
39         if( a[x].top() == b[x].top() )  a[x].pop(), b[x].pop();
40         else break;
41     }
42     if( a[x].empty() ) return -1;
43     else return a[x].top();
44 }
45 int ql, qr, n, m, d;
46 #define mid ((l+r)>>1)
47 inline void add(int l,int r,int k,int flag) {
48     if( ql > qr ) return;
49     if( ql <= l && r <= qr ) { if( flag ) a[k].push(d); else b[k].push(d); return; }
50     if( ql <= mid ) add(l,mid,k<<1,flag); if( qr > mid ) add(mid+1,r,k<<1|1,flag);
51 }
52
53 inline void update(int x,int y,int flag) {
54     int tot = 0;
55     while( top[x] != top[y] ) {
56         if( dep[top[x]] < dep[top[y]] ) swap(x,y);
57         nod[++tot].l = w[top[x]], nod[tot].r = w[x];
58         x = fa[top[x]];
59     }
60     if( dep[x] > dep[y] ) swap(x,y);
61     nod[++tot].l = w[x], nod[tot].r = w[y];
62     sort(nod+1,nod+tot+1);
63     int nowl = 1;
64     rep(i,1,tot) {
65         ql = nowl, qr = nod[i].l - 1;
66         add(1,n,1,flag);
67         nowl = nod[i].r + 1;
68     }
69     ql = nowl, qr = n;
70     add(1,n,1,flag);
71 }
72 inline int query(int l,int r,int k) {
73     if( l == r ) return attop(k);
74     if( ql <= mid ) return max(attop(k),query(l,mid,k<<1));
75     else return max(attop(k),query(mid+1,r,k<<1|1));
76 }
77
78 int q1[maxn<<1], q2[maxn<<1], q3[maxn<<1];
79
80 int main() {
81     n = read(), m = read(); int flag, x, y;
82     rep(i,1,n-1) add(read(),read());
83     dfs1(1); dfs2(1,1);
84     rep(i,1,m) {
85         flag = read();
86         if( !flag ) q1[i] = read(), q2[i] = read(), d = q3[i] = read(), update(q1[i],q2[i],1);
87         else if( flag == 1 ) x = read(), d = q3[x], update(q1[x],q2[x],0);
88         else ql = qr = w[read()], printf("%d\n", query(1,n,1));
89     }
90     return 0;
91 }

整体二分

简单讲一下cdq分治和整体二分的区别吧!(基于我的理解)。 cdq分治通常使用在一些有多个约束条件的场合,为了去掉一些约束条件,而使用。总的来说还是用数据结构维护一些数据,只是用了这种思想后可以大大简化问题(例如把二维的题变成一维之类的),在NlongN时间内解答。而整体二分是二分答案,把问题从一个求值的题,转变成为判定性问题,通过一些途径,判断答案是否能达到某个值。因此整体二分一般应该是用来求最值(或第k大)一类的(我的理解)。

在这里,如果一条路径经过了某个点。 那么它的一个终点(或者两个)必定在以该点为根的子树内。那我们想一下如果两个终点都在以该点为根的子树内,那么该点必定是这两个终点的lca。

而如果两个终点都在以该点为根的子树,而不经过该点,那么该点必定是这两个终点的lca的祖先。因此就可以维护经过某个点的路径有多少。

二分答案K,维护权值大于答案K的路径。假设当前权值大于答案K的路径有num条,而当前经过某个点的路径恰为num条,那么对于这个询问答案必定小于K

 1 #include<cstdio>
 2 #include<iostream>
 3 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 4 #define dow(i,j,k) for(register int i = j; i >= k; i--)
 5 #define ez(i,j) for(edge*i = head[j]; i; i=i->next)
 6 #define maxn 102333
 7 using namespace std;
 8
 9 struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn];
10 inline void add(int x,int y) { pt->to = y, pt->next = head[x], head[x] = pt++; pt->to = x, pt->next = head[y], head[y] = pt++; }
11
12 inline int read() {
13     int s = 0, t = 1; char c = getchar();
14     while( !isdigit(c) ) { if( c == ‘-‘ ) t = -1; c = getchar(); }
15     while( isdigit(c) ) s = s * 10 + c - 48, c = getchar();
16     return s * t;
17 }
18
19 int tot = 0, n, m, dep[maxn], bin[17], fa[maxn][17], t[maxn] = {0}, ti[maxn] = {0}, now = 0, L[maxn], R[maxn];
20 #define lower(x) (x & -x)
21 #define to i->to
22 inline int query(int x) { int ret = 0; while( x ) { if( ti[x] == now ) ret += t[x]; x -= lower(x); } return ret; }
23 inline void update(int x,int d) { while( x <= n ) if( ti[x] == now ) t[x] += d, x += lower(x); else ti[x] = now, t[x] = d, x += lower(x); }
24 inline void dfs(int x) {
25     L[x] = ++tot;
26     rep(i,1,16) if( dep[x] >= bin[i] ) fa[x][i] = fa[fa[x][i-1]][i-1];
27     ez(i,x) if( to != fa[x][0] ) fa[to][0] = x, dep[to] = dep[x] + 1, dfs(to);
28     R[x] = tot;
29 }
30 inline int lca(int x,int y) {
31     if( dep[x] < dep[y] ) swap(x,y);
32     if( dep[x] != dep[y] ) {
33         int dis = dep[x] - dep[y];
34         rep(i,0,16) if( dis & bin[i] ) dis -= bin[i], x = fa[x][i]; else if( !dis ) break;
35     }
36     if( x == y ) return x;
37     dow(i,16,0) if( fa[x][i] != fa[y][i] ) x = fa[x][i], y = fa[y][i];
38     return fa[x][0];
39 }
40
41 struct node{ int x, y, flag, d; } nod[maxn<<1];
42 int ans[maxn<<1], t1[maxn<<1], t2[maxn<<1], bel[maxn<<1];
43 inline void solve(int l,int r,int al,int ar) {
44     if( al == ar ) { rep(i,l,r) ans[bel[i]] = al; return; }
45     int mid = (al + ar) >> 1;
46     int l1 = 0, l2 = 0, num = 0, x, y, z, tot; now++;
47     bool nedl = 0, nedr = 0;
48     rep(i,l,r)
49             if( !nod[bel[i]].flag ) {
50                 if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; }
51                 t2[++l2] = bel[i]; num++; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y);
52                 update(L[x],1); update(L[y],1); update(L[z],-1); if( fa[z][0] ) update(L[fa[z][0]],-1);
53             } else if( nod[bel[i]].flag == 1 ) {
54                 if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; }
55                 t2[++l2] = bel[i]; num--; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y);
56                 update(L[x],-1); update(L[y],-1); update(L[z],1); if( fa[z][0] ) update(L[fa[z][0]],1);
57             } else {
58                 x = nod[bel[i]].x;
59                 tot = query(R[x]) - query(L[x]-1);
60                 if( tot < num ) t2[++l2] = bel[i], nedr = 1;
61                 else t1[++l1] = bel[i], nedl = 1;
62             }
63     rep(i,l,l+l1-1) bel[i] = t1[i-l+1];
64     rep(i,l+l1,r) bel[i] = t2[i-l-l1+1];
65     if( nedl ) solve(l,l+l1-1,al,mid);
66     if( nedr ) solve(l+l1,r,mid+1,ar);
67 }
68
69 int main() {
70     n = read(), m = read();
71     bin[0] = 1; rep(i,1,16) bin[i] = bin[i-1] << 1;
72     rep(i,1,n-1) add(read(),read()); dfs(1);
73     int mx = 0, x;
74     rep(i,1,m) {
75         nod[i].flag = read();
76         if( !nod[i].flag ) nod[i].x = read(), nod[i].y = read(), nod[i].d = read(), mx = max(nod[i].d,mx);
77         else if( nod[i].flag == 1 ) x = read(), nod[i].x = nod[x].x, nod[i].y = nod[x].y, nod[i].d = nod[x].d;
78         else nod[i].x = read();
79     }
80     rep(i,1,m) bel[i] = i, ans[i] = -1;
81     solve(1,m,-1,mx);
82     rep(i,1,m) if( nod[i].flag == 2 ) printf("%d\n", ans[i]);
83     return 0;
84 }
RunID User Problem Result Memory Time Language Code_Length Submit_Time
1433099 CCTVhq 4538 Accepted 19924 kb 8952 ms C++/Edit 3562 B 2016-05-04 19:52:05
1432903 CCTVhq 4538 Accepted 47080 kb 6628 ms C++/Edit 3001 B 2016-05-04 18:30:31

上面那个是整体二分的。

下面是链剖的。

时间: 2024-08-05 02:27:56

4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分的相关文章

【bzoj】4538: [Hnoi2016]网络

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4538 维护一个数据结构支持对于一颗树的操作,需要支持: 1.对于树上的一条路径上的每个点上放一个值. 2.撤销某次操作的路劲放. 3.查询除了经过这个点的路径的最大值. 往一个路径上丢值相当于往不经过条路径的所有点上丢值. 用一个树链剖分即可维护,对于操作区间取反. 直接查询单点最大值即可. 为了维护单点最大值,线段树中的每一个点对应两个堆,用于维护插入誉删除. 防止爆空间,所以标记永久

bzoj 4538: [Hnoi2016]网络

Description 一个简单的网络系统可以被描述成一棵无根树.每个节点为一个服务器.连接服务器与服务器的数据线则看做一条树边.两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身).由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互.此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权.现在,你作为一个网络系统的管理员,要监控整个系统的运行状态.系统的运行也是很简单的,在每一个时刻,只有可能

[CTSC2008]网络管理(整体二分+树剖+树状数组)

一道经典的带修改树链第 \(k\) 大的问题. 我只想出三个 \(\log\) 的解法... 整体二分+树剖+树状数组. 那不是暴力随便踩的吗??? 不过跑得挺快的. \(Code\ Below:\) // luogu-judger-enable-o2 #include <bits/stdc++.h> #define lowbit(x) ((x)&(-(x))) using namespace std; const int maxn=80000+10; const int lim=1e

树链剖分-Hello!链剖-[NOIP2015]运输计划-[填坑]

This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://www.cnblogs.com/JasonCow/ [NOIP2015]运输计划    Hello!链剖.你好吗? 题意: 给出一棵n个节点的带权树,m对树上点对 现在允许删除一条边,(权值修改为0) 输出: 最小化的点对间最大距离 1.链剖 2.树上差分 3.二分 链剖我就不多说了,就是两dfs 注意

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<

【树链剖分】链剖相关总结与心得

这两天连着做了一些链剖也看了不少链剖已经大致明白链剖里题目特点了 链剖题目分类 我们可以把所有链剖的题目分为如下两类: 给定点权的链剖 这类链剖也是最基础的链剖,大部分题目都是这个样子的. 题目中会给定每一个点的初始点权,以此来计算路径长度. 这种题目相对来说比较简单,我们直接套模板两次DFS然后建树,把每个点在线段树上对应节点的数值modify就好了. 标准模板题可以看:ZJOI2008树的统计Count 那就是一个裸的点权链剖 给定边权的链剖 这类链剖相比上面那个会稍微复杂一点. 题目中给定

链剖-进阶ing-填坑-NOIP2013-货车运输

This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://www.cnblogs.com/JasonCow/ 似乎官方给的是倍增lca 不管了,最近练习链剖,以后有时间在补倍增的写法 就是边权下放成点权,然后树链剖分套一颗线段树就可以了 开始sb似的建成一颗大树,其实直接利用Kruskal的并查集查询是否在同一子树就好了[森林x1森林x2森林x3重要的事说三遍

链剖-What you are?-大话西游-校内oj2440

This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://www.cnblogs.com/JasonCow/ 链剖+线段树 所以为什么 2017.4.8 C题爆零了!!! 我的暴力分呢? 大话西游AC code 假装考试30分拿到了T△T 1 #include <algorithm> 2 #include <iostream> 3 #includ

堆 (优先队列)举例

*/--> pre.src {background-color: Black; color: White;} 堆 (优先队列)举例 题目: 你需要驾驶一辆卡车行驶 l 单位距离.最开始的时候,卡车上有 p 单位的汽油.卡车每开 1 单位距离需要消费 1 单位的汽油.如果在途中汽车上的汽油耗尽,卡车就无法继续前行,因而无法到达终点.在途中一共有 n 个加油站.第 i 个加油站在距离起点 ai 单位距离的地方,最多可以给卡车加 bi 单位汽油.假设卡车的燃料箱的容量是无限大的,无论加多少油都没有问题