BZOJ-3531-旅行

描述

http://www.lydsy.com/JudgeOnline/problem.php?id=3531


分析

  • 刚拿到这个题时看到可以更改信仰的宗教, 也就是可以改变路径, 以为是动态树的题目(动态树不会), 后来发现都用的树链剖分, 为每个宗教建立一个线段树. 表示很神奇.
  • 宗教数 <= 10^5, 每个宗教建立一个线段树, 线段树又一般开到四倍空间… 按我平常写线段树的方法肯定不行了. 于是从 HZWER 的博客里学到了动态的线段树, 需要访问某结点时给它分配编号, 记录结点的左右子结点编号而不是用o<<1表示o的左结点, o<<1^1表示o的右结点的方法. 有点指针的感觉.
  • 一开始我卡在不知道怎么修改信仰宗教上, 看 HZWER 发现很简单, 把结点在最初信仰的宗教的线段树里的值修改为0, 再在改变后的信仰宗教的线段树里把值修改为原来的值.
  • 这道题我调了很长时间, 直到最后发现一个最关键的错误出在查询上.
    以查询和为例, 我一开始是这么写的 :
int ask_sum(int x, int y)
{
    int ret = 0;
    while(top[x] != top[y]) {
        if(dep[top[x]] < dep[top[y]]) swap(x, y); // top
        ret += query_sum(roots[c[x]], 1, n, tid[top[x]], tid[x]);
        x = fa[top[x]];
    }

if(dep[x] > dep[y]) swap(x, y);
    ret += query_sum(roots[c[x]], 1, n, tid[x], tid[y]);
    return ret;
}
  • 这里c[x]表示x城市信仰的宗教, 当x改变时, c[x]势必会改变, 但我们要查找的线段树始终是不变的. 所以要记录x最初的线段树的根结点, 而不能每次都用roots[c[x]]来获取.
    改后 :
int ask_sum(int x, int y)
{
    int ret = 0, cx = c[x]; // here
    while(top[x] != top[y]) {
        if(dep[top[x]] < dep[top[y]]) swap(x, y); // top
        ret += query_sum(roots[cx], 1, n, tid[top[x]], tid[x]);
        x = fa[top[x]];
    }

  if(dep[x] > dep[y]) swap(x, y);
    ret += query_sum(roots[cx], 1, n, tid[x], tid[y]);
    return ret;
}
  • 发现vector存图真的没邻接表好, 既费时间又费空间, 所以以后改用数组模拟邻接表来存了.

代码

https://code.csdn.net/snippets/611472

posted @ 2015-03-03 22:34  wfwbz  阅读(93)  评论(0编辑  收藏  举报