HDU 3966 树链剖分

HDU 3966
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3966
题意:
n个(<=1e5)兵营,以树的形式相互连接构成连通图。给出初始每个兵营的兵数量。
然后有修改操作,即u-v的路径上(包括u,v)经过的兵营兵的数量全减少或增加某一个值。
有查询操作,求一个兵营当前兵的数量。输出这个数量。
思路:
裸的树链剖分。
各种写错。
容易T的地方:
1)solve处的u=fa[tp1]写成u=fa[u]
2)Solve处各种写反
容易WA的地方:
1)solve处u=v则退出
2)建线段树时,tree[o].val = val[id[l]]而不是tree[o].val = val[tid[l]](要用一个新的指针指回去,搞清楚线段树中这个点到底是哪一个点)
3)线段树更新时没有及时push_down。
源码:

#pragma comment(linker,"/STACK:100000000,100000000")
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 50000 + 5;
int tp[MAXN], fa[MAXN], num[MAXN], son[MAXN], dep[MAXN];
int id[MAXN], cnt, tid[MAXN];
vector<int>lin[MAXN];
void dfs1(int u, int f, int deep)
{
    num[u] = 1, fa[u] = f, son[u] = 0, dep[u] = deep;
    for(int i = 0 ; i < (int)lin[u].size() ; i++){
        int v = lin[u][i];
        if(v == f)  continue;
        dfs1(v, u, deep + 1);
        num[u] += num[v];
        if(num[son[u]] < num[v])    son[u] = v;
    }
}
void dfs2(int u, int f)
{
    id[u] = ++cnt;
    tid[cnt] = u;
    tp[u] = f;
    if(son[u])  dfs2(son[u], f);
    for(int i = 0 ; i < (int)lin[u].size() ; i++){
        int v = lin[u][i];
        if(v == fa[u] || v == son[u])   continue;
        dfs2(v, v);
    }
}
struct Tree
{
    int l, r, val;
    int add;
}tree[MAXN * 4];
int val[MAXN];
void build(int l, int r, int o)
{
    tree[o].l = l, tree[o].r = r, tree[o].add = 0, tree[o].val = 0;
    if(l == r){
        tree[o].val = val[tid[l]];
        return;
    }
    int mid = (l + r) / 2;
    if(mid >= l)    build(l, mid, o * 2);
    if(mid < r)     build(mid + 1, r, o * 2 + 1);
}
void push_down(int o)
{
    if(tree[o].add == 0)    return;
    tree[o * 2].add += tree[o].add;
    tree[o * 2 + 1].add += tree[o].add;
    tree[o].add = 0;
}
int query(int u, int o)
{
//    printf("u = %d, o = %d\n", u, o);
//    printf("tree[o].l = %d, tree[o].r = %d\n", tree[o].l, tree[o].r);
//    system("pause");
    if(tree[o].l == tree[o].r && tree[o].l == u){
        return tree[o].add + tree[o].val;
    }
    int ans = 0;
    push_down(o);
    int mid = (tree[o].l + tree[o].r) / 2;
    if(mid >= u)    ans = query(u, o * 2);
    else    ans = query(u, o * 2 + 1);
    return ans;
}
void update(int l, int r, int val, int o)
{
//    if(tree[o].r < l || tree[o].l > r)  return;
//    printf("l = %d, r = %d, val = %d, tree[o].l = %d, tree[o].r = %d, o = %d\n", l, r, val, tree[o].l, tree[o].r, o);
//    system("pause");
    if(tree[o].l >= l && tree[o].r <= r){
        tree[o].add += val;
        return;
    }
    push_down(o);
    int mid = (tree[o].l + tree[o].r) / 2;
    if(mid < l) update(l, r, val, o * 2 + 1);
    else if(mid >= r)  update(l, r, val, o * 2);
    else{
        update(l, r, val, o * 2);
        update(l, r, val, o * 2 + 1);
    }
}
void solve(int u, int v, int val)
{
    int tp1 = tp[u], tp2 = tp[v];
    while(tp1 != tp2){
        if(dep[tp1] < dep[tp2]) swap(tp1, tp2), swap(u, v);
        update(id[tp1], id[u], val, 1);
        u = fa[tp1];
        tp1 = tp[u];
    }
    if(dep[u] > dep[v]) swap(u, v);
    update(id[u], id[v], val, 1);
}
char op[1000];
int main()
{
    int n, m, q;
    while(scanf("%d%d%d", &n, &m, &q) != EOF){
        for(int i = 0 ; i <= n ; i++)   lin[i].clear();
        int u, v, w;
        for(int i = 1 ; i <= n ; i++)   scanf("%d", &val[i]);
        for(int i = 0 ; i < m ; i++){
            scanf("%d%d", &u, &v);
            lin[u].push_back(v);
            lin[v].push_back(u);
        }
        cnt = 0;
        dfs1(1, 0, 1);
        dfs2(1, 1);
        build(1, n, 1);
        while(q--){
            scanf("%s", op);
            if(op[0] == 'Q'){
                scanf("%d", &u);
//                printf("first = %d, second = %d\n", query(id[u], 1), val[id[u]]);
                printf("%d\n", query(id[u], 1));
            }
            else{
                scanf("%d%d%d", &u, &v, &w);
                if(op[0] == 'D')    solve(u, v, -w);
                else    solve(u, v, w);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值