SPOJ-375-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 }
View Code

 

转载于:https://www.cnblogs.com/ZiningTang/p/4021425.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值