poj 2763 Housewife Wind

31 篇文章 0 订阅
2 篇文章 0 订阅

Problem

poj.org/problem?id=2763
vjudge.net/contest/28982#problem/B

Reference

poj2763(树链剖分 - 边权)

Meaning

一棵 n 个点的树,每条边都有一个边权。一个人开始在 s 结点。两种操作:

  • 0 u:询问人从 s 走到 u 要走多久,之后人就走到了 u 这个点(即 s 更新为 u)
  • 1 i w:把第 i 条边的权值改成 w

Analysis

考虑树链剖分。权值是在边上,要把权值“转移”到点上
策略是:对于每一条边 e 的两个端点 f 和 t,如果 depth[f] > depth[t],就把边权记到 f 上,否则记到 t 上(即把边权转移到深度更大的端点处)。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000;

int head[N+1], from[N<<1], to[N<<1], nxt[N<<1], w[N<<1];

void add_edge(int f, int t, int c, int sz)
{
    from[sz] = f;
    to[sz] = t;
    w[sz] = c;
    nxt[sz] = head[f];
    head[f] = sz;
}

int dep[N+1], fa[N+1], son[N+1], tsz[N+1];
int tid[N+1], pos[N+1], top[N+1], tm;

void heavy_light(int v, int f, int d)
{
    dep[v] = d;
    fa[v] = f;
    tsz[v] = 1;
    son[v] = -1;
    for(int i = head[v]; ~i; i = nxt[i])
        if(to[i] != f)
    {
        heavy_light(to[i], v, d + 1);
        tsz[v] += tsz[to[i]];
        if(son[v] == -1 || tsz[to[i]] > tsz[son[v]])
            son[v] = to[i];
    }
}

void decompose(int v, int t)
{
    top[v] = t;
    pos[v] = ++tm;
    tid[pos[v]] = v;
    if(son[v] == -1)
        return;
    decompose(son[v], t);
    for(int i = head[v]; ~i; i = nxt[i])
        if(to[i] != fa[v] && to[i] != son[v])
            decompose(to[i], to[i]);
}

int tree[N+1<<2];

inline void pushup(int x)
{
    tree[x] = tree[x<<1] + tree[x<<1|1];
}

void update(int p, int v, int l, int r, int rt)
{
    if(l == r)
    {
        tree[rt] = v;
        return;
    }
    int m = l + r >> 1;
    if(p > m)
        update(p, v, m+1, r, rt<<1|1);
    else
        update(p, v, l, m, rt<<1);
    pushup(rt);
}

int sum(int ql, int qr, int l, int r, int rt)
{
    if(ql <= l && r <= qr)
        return tree[rt];
    if(qr < l || r < ql)
        return 0;
    int m = l + r >> 1;
    return sum(ql, qr, l, m, rt<<1) +
        sum(ql, qr, m+1, r, rt<<1|1);
}

int query(int l, int r, int n)
{
    int ans = 0;
    while(top[l] != top[r])
    {
        if(dep[top[l]] < dep[top[r]])
            swap(l, r);
        ans += sum(pos[top[l]], pos[l], 1, n, 1);
        l = fa[top[l]];
    }
    if(l != r) // 必须要这个判断
    {
        if(dep[l] > dep[r])
            swap(l, r);
        // 当处于同一条链
        // 上面的那端要往下退一个点 -> son[l]
        // 因为 l 点记得权值是它上面那条边的
        // 而那条边不在查询路径上
        ans += sum(pos[son[l]], pos[r], 1, n, 1);
    }
    return ans;
}

int a[N+1] = {0}; // 结点 i 的初始权值
int mp[N]; // 第 i 条边的权值记载 mp[i] 结点上

void build(int l, int r, int rt)
{
    if(l == r)
    {
        tree[rt] = a[tid[l]];
        return;
    }
    int m = l + r >> 1;
    build(l, m, rt<<1);
    build(m+1, r, rt<<1|1);
    pushup(rt);
}

int main()
{
    int n, q, s;
    scanf("%d%d%d", &n, &q, &s);
    memset(head, ~0, sizeof head);
    for(int i = 1, f, t, c, sz = 0; i < n; ++i)
    {
        scanf("%d%d%d", &f, &t, &c);
        add_edge(f, t, c, sz++);
        add_edge(t, f, c, sz++);
    }

    heavy_light(1, 0, 1);
    tm = 0;
    decompose(1, 1);

    // 对每一条边,都映射到它深度更大的那个端点
    // 并把它的权值记载那个映射点上
    for(int i = 1, j = 0; i < n; ++i, j += 2)
        if(dep[from[j]] > dep[to[j]])
            a[mp[i] = from[j]] = w[j];
        else
            a[mp[i] = to[j]] = w[j];

    build(1, n, 1);

    for(int op, v, e; q--; )
    {
        scanf("%d", &op);
        if(op == 0)
        {
            scanf("%d", &v);
            printf("%d\n", query(s, v, n));
            s = v;
        }
        else
        {
            scanf("%d%d", &e, &v);
            // 更新边就变成更新点
            // mp[e] 找到要更新的点
            update(pos[mp[e]], v, 1, n, 1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值