SPOJ-Qtree-树链剖分(边的剖分)

【前言】TTvTT先让我呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜一下。。。。。。经历了5发WA,6发RE,3发TLE后,今天终于和这道题做了个了断了。

题意:一棵树,给出边权值,有两种操作:更改一条边的值;查找a到b路径上的最大边权值。

【唧唧喳喳】这道题算是树链剖分对边剖分的一道很好的训练题吧,但是数据好像比较变态很容易TLE的样子,嘛啊,其实我也布吉岛。

【思路】: 对于一棵树,每个节点(除了根节点)都只有一个父亲,那么对于每条边,做儿子的那个节点用来记录边权值。大体就是这样。TvT然后查询的时候,注意和点的剖分的不同的是,查询的时候比较的是链首节点的深度,因为边的剖分不同于点的剖分,比较深度的点存的是与其父节点的连边,所以如果比较最下面的节点,当来到LCA的时候就会往上走多了一个不必走的节点,会出错。

【总结】:开始用map存节点来对应边,但是TLE了,然后改成用了pre和fun1来对应输入的各边和输入的节点的关系,用tree和fun2对应编号节点和编号。这才过了。

AC代码(420ms):

【布吉岛是不是数据变弱了,我用420ms才过,看有些菊苣用了2.6s_(:з」∠)_】

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <climits>
  5 using namespace std;
  6 int const maxn = 10009;
  7 int pre[maxn], n, edge, cnt, fa[maxn], deep[maxn], num[maxn], son[maxn], tree[maxn], fun1[maxn], fun2[maxn];
  8 int to[maxn<<1], next[maxn<<1], head[maxn], top[maxn], seq[maxn<<1], cost[maxn];
  9 void add(int u, int v, int num)
 10 {
 11     to[edge] = v;
 12     next[edge] = head[u];
 13     seq[edge] = num;
 14     head[u] = edge++;
 15 }
 16 void init()
 17 {
 18     scanf("%d", &n);
 19     memset(head, -1, sizeof(head));
 20     memset(son, -1, sizeof(son));
 21     edge = cnt = 0;
 22     for(int i = 1; i < n; i++) {
 23         int a, b; scanf("%d%d%d", &a, &b, &cost[i]);
 24         add(a, b, i); add(b, a, i);
 25     }
 26     fa[1] = deep[1] = tree[1] = 1;
 27 }
 28 void dfs1(int u)
 29 {
 30     num[u] = 1;
 31     for(int e = head[u]; ~e; e = next[e]) {
 32         int too = to[e];
 33         if(fa[u] != too) {
 34             pre[seq[e]] = too;      //把输入的边和较深的节点对应起来
 35             fun1[too] = seq[e];     //存节点对应的边
 36             deep[too] = deep[u]+1;
 37             fa[too] = u;
 38             dfs1(too); num[u] += num[too];
 39             if(son[u] == -1||num[son[u]] < num[too]) son[u] = too;
 40         }
 41     }
 42 }
 43 void dfs2(int u, int lead)
 44 {
 45     top[u] = lead;
 46     if(u != 1) tree[u] = ++cnt, fun2[cnt] = u; //剖分的编号一一对应
 47     if(son[u] == -1) return;
 48     dfs2(son[u], lead);
 49     for(int e = head[u]; ~e; e = next[e]) {
 50         int too = to[e];
 51         if(fa[u] != too && son[u] != too) dfs2(too, too);
 52     }
 53 }
 54 #define lson l, m, rt<<1
 55 #define rson m+1, r, rt<<1|1
 56 int sgt[maxn<<2] = {INT_MIN};
 57 void push_up(int rt)
 58 {
 59     sgt[rt] = max(sgt[rt<<1], sgt[rt<<1|1]);
 60 }
 61 void build(int l, int r, int rt)
 62 {
 63     if(l == r) {
 64         int pos = fun1[fun2[l]];
 65         sgt[rt] = cost[pos]; return;
 66     }
 67     int m = (l+r)>>1;
 68     build(lson); build(rson);
 69     push_up(rt);
 70 }
 71 void change(int l, int r, int rt, int pos, int val)
 72 {
 73     if(l == r) { sgt[rt] = val; return; }
 74     int m = (l+r)>>1;
 75     if(pos <= m) change(lson, pos, val);
 76     if(m < pos) change(rson, pos, val);
 77     push_up(rt);
 78 }
 79 int query(int l, int r, int rt, int L, int R)
 80 {
 81     if(L <= l && r <= R) return sgt[rt];
 82     int m = (l+r)>>1;
 83     int a, b; a = b = INT_MIN;
 84     if(L <= m) a = query(lson, L, R);
 85     if(m < R) b = query(rson, L, R);
 86     return max(a, b);
 87 }
 88 int answer(int a, int b)
 89 {
 90     if(a == b) return 0;
 91     int ans = INT_MIN;
 92     while(top[a] != top[b]) {
 93         if(deep[top[a]] < deep[top[b]]) swap(a, b);
 94         ans = max(ans, query(1, n-1, 1, tree[top[a]], tree[a]));
 95         a = fa[top[a]];
 96     }
 97     if(deep[a] > deep[b]) swap(a, b);
 98     if(a != b) ans = max(ans, query(1, n-1, 1, tree[son[a]], tree[b]));
 99     return ans;
100 }
101 void work()
102 {
103     init();
104     dfs1(1); dfs2(1, 1); build(1, n-1, 1);
105     char s[20];
106     while(scanf("%s", s) && s[0] != ‘D‘) {
107         int a, b; scanf("%d%d", &a, &b);
108         if(s[0] == ‘C‘) {
109             change(1, n-1, 1, tree[pre[a]], b);
110         }
111         else {
112             printf("%d\n", answer(a, b));
113         }
114     }
115 }
116 int main()
117 {
118     int t; cin>>t;
119     while(t--) work();
120     return 0;
121 }

时间: 2024-08-03 05:14:08

SPOJ-Qtree-树链剖分(边的剖分)的相关文章

【学术篇】SPOJ QTREE 树链剖分

发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能得28pts.. 重新见到这些失踪的dalao灰常开心, 于是想让自己心情稍微差一点, 就想着把自己昨天写WA的QTREE重构一遍吧.. 于是重构的sb链剖果然挂掉了... 出现了各种各样的漏洞... 忘记各种各样的句子, 然而退化成了暴力小数据也随便过看不出来啊~~~ 但是还是在1h之内调对了_(

SPOJ QTREE 树链剖分

375. Query on a tree Problem code: QTREE You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

SPOJ375.QTREE树链剖分

题意:一个树,a b c 代表a--b边的权值为c.CHANGE x y  把输入的第x条边的权值改为y,QUERY x y 查询x--y路径上边的权值的最大值. 第一次写树链剖分,其实树链剖分只能说是一种思想.树链剖分  就是 先选择从根节点到叶子节点的最长的路径的权值对应到线段树上,然后从一个子树的根节点到叶子的最长路径的权值对应到线段树上这样直到把所有的点都处理了,然后就是线段树区间查询最值了. 具体可以看这个博客.http://blog.sina.com.cn/s/blog_6974c8

SPOJ 375 树链剖分

点击打开链接 题意:给个树和树上的权值,两个操作,Q u v,问u到v的边上的最大权值,C u v,将第u条边的权值改为v 思路:今天学了学树链剖分,这题是个检验模版的题目,理论我是解释不清楚的,自己在九野聚聚那学来的一份模版 #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <a

SPOJ 375 QTREE系列-Query on a tree (树链剖分)

题目地址:SPOJ 375 树链剖分第一发! 果然是个貌似很高级的数据结构,其实就是把树的边从树形结构转化成了线性结构,从而可以用线段树或树状数组之类的数据结构进行快速维护.从而将时间缩到n*log(2*n). 这题用的线段树维护的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #incl

[学习笔记]树链剖分

基本思想 树链剖分一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链. 一些定义 树链:树上的路径. 剖分:把路径分类为重链和轻链. 重儿子:u的子节点中siz[v]值最大的v. 轻儿子:u的其它子节点. 重边:点u与其重儿子的连边. 轻边:点u与其轻儿子的连边. 重链:由重边连成的路径. 轻链:轻边. 性质 如果(u,v)为轻边,则siz[v]$\times$2<siz[u]. 从根到某一点的路径上轻链.重链的个数都不大于l

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

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

SPOJ 375 QTREE - Query on a tree(树链剖分)

题目链接:http://www.spoj.com/problems/QTREE/en/ 题意:  一棵树 n 个节点,每条边上有权值,同时有两个操作: (1)更改操作:CHANGE i ti(把第 i 条边上的权值改为 ti). (2)查询操作:QUERY a b(查询 a 到 b 的路径上权值最大的边的权值). 思路(树链剖分): 看到这种区间查询的题想要用数据结构优化,提高时间效率一般会想到线段树.可是这次操作的对象并不是一组序列, 无法直接使用线段树.这时,我们可以做些转化:对树作树链剖分

SPOJ - QTREE 375 Query on a tree 树链剖分+线段树

操作1:修改第k条边权. 操作2:询问两点间最大边权. 树链剖分,然后线段树维护最大值 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #inclu

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]