zoukankan      html  css  js  c++  java
  • hdu-3699-Aragorn's Story-樹鏈剖分模板題

    樹鏈剖分學習blog:http://blog.csdn.net/jiangshibiao/article/details/24669751

    關於這題的學習blog:http://blog.csdn.net/acdreamers/article/details/10594121

    下面來說說樹鏈剖分の我的理解。

    作為一個樹鏈剖分的初學者,看到個大牛的博客里一上來就描述做法,稍顯得有點吃不消,嚼了好幾天的資料才總算看懂。作為一個初學者,以初學的狀態,透過現象看本質才能更好地上手,因為只有知道它能做什麼,才會順著思考怎麼做,才能更順利地轉化成自己的代碼思路。

    按我的理解,樹鏈剖分準確來說不是一種數據結構,只是一種思想罷了,做過 poj3321、和 hdu3333之類的題或許會更有感觸,樹鏈剖分也只是像dfs序一樣,用一種方法將一棵樹更好地編碼(不管是邊或節點)然後用別的數據結構高效維護。

    下面講講實現,由於我目前只寫了關於點的剖分(還有剖分邊的),所以以下以節點的剖分做講解,維護用線段樹(這裡默認你懂線段樹了)。

    樹鏈剖分也叫輕重鏈剖分,即是把一棵樹按子節點的孩子個數分為輕重鏈,父節點下的兒子數目多的節點為重兒子,其餘的都為輕兒子。標註dfs序的時候,我們先標註完重鏈(都是重兒子組成的鏈)上的點,再標註輕鏈,這樣,對於一條重鏈,其節點在線段樹中的排列是連續的。我們在查詢的時候,如果節點在同一條重鏈上,即可直接查詢。到這裡,用兩個dfs預處理完, 就完成了對一棵樹的剖分了。具體實現請轉上面的blog。就不重複造輪子了。

    接著是修改查詢操作。修改一條樹上的路徑,由上預處理操作我們可以知識遷移一下,對於在同一條重鏈里的路徑,我們可以按dfs出來的映射直接在線段樹中修改;如果不在同一重鏈,由於樹上的路徑唯一,我們可以從更深的節點開始往上修改,直到兩者在一條鏈(也可能是一個點)中,即是修改更深的節點所在的重鏈(如果節點是輕邊上的葉子節點,則修改的只是它自己)然後將節點賦值為該重鏈的父親節點,重複操作直到兩點在同一重鏈(這裡用到dfs求出的top[]——重鏈首節點、fa[]——節點的直接父節點,父親節點和重鏈首節點連的是一條輕邊,自己想想為什麼,其實畫畫圖就知道了)。對於查詢也是同理。但是杭電這道3699由於是單點查詢,所以省了好多麻煩。

    對於樹鏈剖分的分析就到這了,實現請移步上面的blog,在做之前最好自己摸清思路,按照作者思路先實現一遍再看別人代碼,這才能更好地發現自己思維上的不足。

    講完之後,這道題實際上就是一道水水的模板題辣。

    AC代碼:

      1 #pragma comment(linker, "/STACK:1024000000,1024000000")
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <algorithm>
      6 using namespace std;
      7 const int maxn = 50010;
      8 int son[maxn], deep[maxn], num[maxn], tree[maxn], pre[maxn], fa[maxn], top[maxn];
      9 int to[maxn<<1], next[maxn<<1], head[maxn];
     10 int n, m, p, edge, cnt, arr[maxn];
     11 void add(int a, int b)   //数组模拟邻接表存图
     12 {
     13     to[edge] = b; next[edge] = head[a]; head[a] = edge++;
     14 }
     15 void init()
     16 {
     17     edge = 0;
     18     cnt = 0;
     19     top[1] = 1; deep[1] = 1; num[1] = 1; fa[1] = 1;
     20     memset(head, -1, sizeof(head));
     21     memset(son, -1, sizeof(son));
     22 }
     23 void dfs1(int u)
     24 {
     25     num[u] = 1;
     26     for(int e = head[u]; ~e; e = next[e]){
     27         int too = to[e];
     28         if(too != fa[u]) {
     29             fa[too] = u; deep[too] = deep[u]+1;
     30             dfs1(too);
     31             num[u] += num[too];  //儿子数
     32             if(son[u] == -1||num[too] > num[son[u]]) son[u] = too;   //son[u]存u的重儿子节点编号
     33         }
     34     }
     35 }
     36 void dfs2(int u, int lead)
     37 {
     38     top[u] = lead;    //重链的链首节点
     39     tree[u] = ++cnt;   //tree存线段树中节点编号
     40     pre[tree[u]] = u;   //pre存线段树编号对应的树节点编号
     41     if(son[u] == -1) return;
     42     dfs2(son[u], lead);
     43     for(int e = head[u]; ~e; e = next[e]) {
     44         int too = to[e];
     45         if(son[u] != too&&fa[u] != too) dfs2(too, too);
     46     }
     47 }
     48 
     49 #define lson l, m, rt<<1
     50 #define rson m+1, r, rt<<1|1
     51 int sgt[maxn<<2], lazy[maxn<<2];  //以下是线段树
     52 void push_down(int rt)
     53 {
     54     if(lazy[rt] != 0) {
     55         lazy[rt<<1] += lazy[rt];
     56         lazy[rt<<1|1] += lazy[rt];
     57         lazy[rt] = 0;
     58     }
     59 }
     60 void build(int l, int r, int rt)
     61 {
     62     lazy[rt] = 0; sgt[rt] = 0;
     63     if(l == r) {
     64         sgt[rt] = arr[pre[l]];
     65         return ;
     66     }
     67     int m = (l+r)>>1;
     68     build(lson); build(rson);
     69 }
     70 void update(int l, int r, int rt, int L, int R, int val)
     71 {
     72     if(L <= l && r <= R) {
     73         lazy[rt] += val;
     74         return;
     75     }
     76     push_down(rt);
     77     int m = (l+r)>>1;
     78     if(L <= m) update(lson, L, R, val);
     79     if(m < R) update(rson, L, R, val);
     80 }
     81 void change(int L, int R, int val)
     82 {
     83     while(top[L] != top[R]) {    //直到在同一链中
     84         if(deep[top[L]] < deep[top[R]]) swap(L, R);   //先处理更深的节点
     85         update(1, n, 1, tree[top[L]], tree[L], val);
     86         L = fa[top[L]];
     87     }
     88     if(deep[L] > deep[R]) swap(L, R);
     89     update(1, n, 1, tree[L], tree[R], val);
     90 }
     91 int query(int l, int r, int rt, int pos)
     92 {
     93     if(l == r) {
     94         sgt[rt] += lazy[rt];
     95         lazy[rt] = 0;
     96         return sgt[rt];
     97     }
     98     push_down(rt);
     99     int m = (l+r)>>1;
    100     int res;
    101     if(pos <= m) res = query(lson, pos);
    102     else res = query(rson, pos);
    103     return res;
    104 }
    105 int main()
    106 {
    107     while(scanf("%d%d%d", &n, &m, &p) != EOF) {
    108         init();
    109         for(int i = 1; i <= n; i++) {
    110             scanf("%d", &arr[i]);
    111         }
    112         for(int i = 0; i < m ;i++) {
    113             int a, b;
    114             scanf("%d%d", &a, &b);
    115             add(a, b); add(b, a);
    116         }
    117         dfs1(1);
    118         dfs2(1, 1);
    119         build(1, n, 1);
    120         while(p--) {
    121             char s[3]; int c1, c2, k;
    122             scanf("%s%d", s, &c1);
    123             if(s[0] == 'Q') {
    124                 printf("%d
    ", query(1, n, 1, tree[c1]));
    125             }
    126             else {
    127                 scanf("%d%d", &c2, &k);
    128                 if(s[0] == 'D') k = -k;
    129                 change(c1, c2, k);
    130             }
    131         }
    132     }
    133     return 0;
    134 }
    View Code
  • 相关阅读:
    C#中的取整函数
    java代码(8) ---guava字符串工具
    java代码(7) ---guava之Bimap
    java代码(6) ---guava之multimap
    java代码(5) ---guava之Multiset
    java代码(4)---guava之Immutable(不可变)集合
    java代码(2)---Java8 Stream
    java代码(3)----guava复写Object常用方法
    java代码(1)---Java8 Lambda
    看完这篇HTTP,跟面试官扯皮就没问题了(转)
  • 原文地址:https://www.cnblogs.com/ZiningTang/p/3992257.html
Copyright © 2011-2022 走看看