zoukankan      html  css  js  c++  java
  • 从lca到树链剖分 bestcoder round#45 1003

    bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次。
    如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或),高手就不这么做了,直接树链剖分。
    为什么不能用lca,因为如果有树退化成链,那么每次询问的复杂度是O(n), 那么q次询问的时间复杂度是O(qn)

    什么是树链剖分呢? 就是把树的边分成轻链和重链

    http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html
    http://www.cnblogs.com/BLADEVIL/p/3479713.html
    这两个博客写的很好了

    剖分后的树有如下性质:
    1、如果(u,v)为轻边, 那么 size[v]*2<size[u]
    2、从根到某一点的路径上的轻链,重链的个数不大于logn
    所以我们可以用线段树来维护这些链,这样如果 要得到任意两点树上路径的信息,那么只要访问线段树,那么时间复杂度只要4logn*logn

    第一次dfs进行树链剖分,求出了轻链和重链
    第二次dfs对树的结点进行了重新编号(结点的编号就是在线段树中区间中的一点), 重链上的结点的编号是连续的
    那么重链上的点在线段树中的区间是连续的。

    那么要询问树上任意两点路径权值的最大值, 只要上depth大的那个点往上走, 如果往上走的时候,遇到的重链,那么可以进去区间查询,然后一次
    走过重链即可。


    对于bestcoder round#45 1003 题,
    第二次dfs求出编号后,建线段树,然后区间的值等于子区间的异或
    那么任意两点的树上路径的权值是否重复,只要访问线段树区间,看最后的异或的值是为0

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <algorithm>
      5 #include <iostream>
      6 #include <queue>
      7 #include <stack>
      8 #include <vector>
      9 #include <map>
     10 #include <set>
     11 #include <string>
     12 #include <math.h>
     13 using namespace std;
     14 #pragma comment(linker, "/STACK:1024000000,1024000000")
     15 #pragma warning(disable:4996)
     16 typedef long long LL;                   
     17 const int INF = 1<<30;
     18 void input(int &x)
     19 {
     20     char ch = getchar();
     21     while (ch<'0' || ch>'9')
     22         ch = getchar();
     23     x = 0;
     24     while (ch >= '0'&&ch <= '9')
     25     {
     26         x = x * 10 + ch - '0';
     27         ch = getchar();
     28     }
     29 }
     30 /*
     31 第一次dfs进行树链剖分,求出了轻链和重链
     32 第二次dfs对树的结点进行了重新编号, 重链上的结点的编号是连续的
     33 那么重链上的点在线段树中的区间是连续的
     34 剖分后的树有如下性质:
     35 性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];
     36 性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。
     37 
     38 那么要询问树上任意两点路径权值的最大值, 只要上depth大的那个点往上走, 如果往上走的时候,遇到的重链,那么可以进去区间查询,然后一次
     39 走过重链
     40 根据性质2:每次询问的复杂度是O(logn*logn)
     41 */
     42 const int N = 100000 + 10;
     43 int size[N], depth[N], son[N], fa[N], w[N], top[N], ra[N], num;
     44 int value[N];
     45 int tree[N * 4];
     46 vector<int> g[N];
     47 int ans;
     48 void dfs(int u)
     49 {
     50     size[u] = 1;
     51     son[u] = 0;
     52     for (int i = 0; i < g[u].size(); ++i)
     53     {
     54         int v = g[u][i];
     55         if (v != fa[u])
     56         {
     57             depth[v] = depth[u] + 1;
     58             fa[v] = u;
     59             dfs(v);
     60             size[u] += size[v];
     61             if (size[v]>size[son[u]])
     62                 son[u] = v;
     63         }
     64     }
     65 }
     66 void dfs2(int u, int tp)
     67 {
     68     top[u] = tp;
     69     w[u] = ++num;//对树上的结点进行编号
     70     ra[num] = u;
     71     //因为优先dfs重儿子,所以一条重链上的点的编号是连续的
     72     if (son[u] != 0)
     73         dfs2(son[u], top[u]);
     74     for (int i = 0; i < g[u].size(); ++i)
     75     {
     76         int v = g[u][i];
     77         if (v != son[u] && v != fa[u])
     78             dfs2(v, v);
     79     }
     80 }
     81 void pushUp(int rt)
     82 {
     83     tree[rt] = tree[rt << 1] ^ tree[rt << 1 | 1];
     84 }
     85 void build(int l, int r, int rt)
     86 {
     87     if (l == r)
     88     {
     89         tree[rt] = value[ra[l]];//ra[l] 是编号为l的是哪个结点
     90         return;
     91     }
     92     int mid = (l + r) >> 1;
     93     build(l, mid, rt << 1);
     94     build(mid + 1, r, rt << 1 | 1);
     95     pushUp(rt);
     96 }
     97 //修改某个结点的值, 那么要将父区间异或旧的值(即去除原先的值),然后异或新的值
     98 void update(int l, int r, int rt, int pos, int newVal, int oldVal)
     99 {
    100     tree[rt] = tree[rt] ^ oldVal^newVal;
    101     if (l == r)
    102         return;
    103     int mid = (l + r) >> 1;
    104     if (pos <= mid)
    105         update(l, mid, rt << 1, pos, newVal, oldVal);
    106     else
    107         update(mid + 1, r, rt << 1 | 1, pos, newVal, oldVal);
    108 }
    109 void query(int l, int r, int rt, int L, int R)
    110 {
    111     if (L <= l && R >= r)
    112     {
    113         ans ^= tree[rt];
    114         return;
    115     }
    116     int mid = (l + r) >> 1;
    117     if (L <= mid)
    118         query(l, mid, rt << 1, L, R);
    119     if (R > mid)
    120         query(mid + 1, r, rt << 1 | 1, L, R);
    121 }
    122 int main()
    123 {
    124     int t, n, q, op,a, b;
    125     scanf("%d", &t);
    126     while (t--)
    127     {
    128         num = 0;
    129         scanf("%d%d", &n, &q);
    130         for (int i = 1; i <= n; ++i)
    131             g[i].clear();
    132         for (int i = 1; i < n; ++i)
    133         {
    134             scanf("%d%d", &a, &b);
    135             g[a].push_back(b);
    136             g[b].push_back(a);
    137         }
    138         for (int i = 1; i <= n; ++i)
    139         {
    140             //scanf("%d", &value[i]);
    141             input(value[i]);
    142             value[i]++;
    143         }
    144         depth[1] = fa[1] = 0;
    145         dfs(1);
    146         dfs2(1, 1);
    147         build(1, n, 1);
    148         while (q--)
    149         {
    150             scanf("%d%d%d", &op, &a, &b);
    151             
    152             if (op == 0)
    153             {
    154                 b++;
    155                 update(1, n, 1, w[a], b, value[a]);
    156                 value[a] = b;
    157             }
    158             else
    159             {
    160                 ans = 0;
    161                 //不停的往上走,像lca一样,不过比lca更优,因为有重链的存在,可以一次走很多部
    162                 while (top[a] != top[b])
    163                 {
    164                     if (depth[top[a]] < depth[top[b]])
    165                         swap(a, b);
    166                     query(1, n, 1, w[top[a]], w[a]);
    167                     a = fa[top[a]];
    168                 }
    169                 if (depth[a] > depth[b])
    170                     swap(a, b);
    171                 query(1, n, 1, w[a], w[b]);
    172                 if (ans == 0)
    173                     printf("%d
    ", -1);
    174                 else
    175                     printf("%d
    ", ans - 1);
    176             }
    177         }
    178     }
    179     return 0;
    180 }
    View Code
  • 相关阅读:
    支付宝校园一卡通充值服务体验
    商品筛选导航菜单亮点欣赏
    手机QQ v4.2 有感
    因所缺,而所需——互联网应用的开发方向
    浅谈 css3 box盒子模型以及box-flex的使用
    浅谈stylus与sass的对比
    css3 transfrom使用以及其martix(矩阵)属性与其它属性的关系
    js命名空间
    公用的stringUtil工具
    js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4623313.html
Copyright © 2011-2022 走看看