设为首页 加入收藏

TOP

BZOJ-3531-旅行
2015-07-20 17:13:35 来源: 作者: 【 】 浏览:2
Tags:BZOJ-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城www.2cto.com市信仰的宗教, 当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存图真的没邻接表好, 既费时间又费空间, 所以以后改用数组模拟邻接表来存了.

?

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++拾遗--函数重载 下一篇Cleaning Shifts (poj 2376 贪心)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·【C语言】动态内存管 (2025-12-27 06:23:20)
·C语言中的内存管理 - (2025-12-27 06:23:16)
·C语言指南:C语言内 (2025-12-27 06:23:14)
·Redis on AWS:Elast (2025-12-27 04:19:30)
·在 Spring Boot 项目 (2025-12-27 04:19:27)