HDU 5044 Tree 树链剖分

一棵树,初始边权和点权都为0

现在有m个操作,每一个操作:

ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k.

ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k.

操作完后,输出每一个点的点权和每一条边的边权(边权按照输入顺序输出)

我们把边权也当做点权处理

这道题本来是一道痕裸的树链剖分的题目,用树状数组维护就可以了O(nlogn*logn)

不过这样会T,因为出题人特意卡了常数

有人是加了一个输入输出外挂过的

不过这道题有更好的做法

我们在树链剖分后的数组用标记法,每次对一个连续区间+k时,我们对位置L标记+k,对位置R+1标记-k

最后从前往后加一遍就是了

O(nlogn)

注意:处理边权和点权时,对lca的处理是不同的

点权时,lca的点权也要更新

边权时,lca对应的边的边权时不需要更新的

注意2:当节点个数为1时,是没有边的,也就没有边权了,但是输出的时候我们边权这一行依然要输出,是一个空行

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>

#define LL long long
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;

const int maxn=1e5+10;

int dep[maxn];
int siz[maxn];
int chg[maxn];
int fa[maxn];
int son[maxn];
int top[maxn];
LL c[2][maxn];
int n;
int e[maxn][2];

struct Edge
{
    int to,next;
};
Edge edge[maxn<<1];
int head[maxn];
int tot;

void init()
{
    memset(head,-1,sizeof head);
    tot=0;
}

void addedge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void solve(int );

int main()
{
    int test;
    int cas=1;
    scanf("%d",&test);
    while(test--){
        printf("Case #%d:\n",cas++);
        init();
        int q;
        scanf("%d %d",&n,&q);
        for(int i=1;i<n;i++){
            scanf("%d %d",&e[i][0],&e[i][1]);
            addedge(e[i][0],e[i][1]);
            addedge(e[i][1],e[i][0]);
        }
        solve(q);
    }
    return 0;
}

void dfs0(int u,int pre)
{
    dep[u]=dep[pre]+1;
    fa[u]=pre;
    siz[u]=1;
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre)
            continue;
        dfs0(v,u);
        siz[u]+=siz[v];
        if(son[u]==-1 || siz[v]>siz[son[u]])
            son[u]=v;
    }
}

void dfs1(int u,int tp)
{
    top[u]=tp;
    chg[u]=++tot;
    if(son[u]==-1)
        return ;
    dfs1(son[u],tp);
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].to;
        if(v==fa[u] || v==son[u])
            continue;
        dfs1(v,v);
    }
}

void update_path(int a,int b,int cnt,int k)
{
    while(top[a]!=top[b]){
        if(dep[top[a]]<dep[top[b]])
            swap(a,b);
        c[cnt][chg[top[a]]]+=k;
        c[cnt][chg[a]+1]-=k;
        a=fa[top[a]];
    }
    if(dep[a]<dep[b])
        swap(a,b);
    if(cnt==1){
        c[cnt][chg[b]]+=k;
        c[cnt][chg[a]+1]-=k;
    }
    else{
        c[cnt][chg[b]+1]+=k;
        c[cnt][chg[a]+1]-=k;
    }
    return ;
}

void solve(int q)
{
    memset(c,0,sizeof c);
    memset(dep,0,sizeof dep);
    memset(son,-1,sizeof son);

    dfs0(1,1);
    tot=0;
    dfs1(1,1);

    for(int i=1;i<=q;i++){
        char str[10];
        scanf("%s",str);
        int u,v,k;
        scanf("%d %d %d",&u,&v,&k);
        if(str[3]==‘1‘){
            update_path(u,v,1,k);
        }
        else{
            update_path(u,v,0,k);
        }
    }

    for(int i=1;i<=n;i++){
        c[0][i]+=c[0][i-1];
        c[1][i]+=c[1][i-1];
    }
    for(int i=1;i<=n;i++){
        printf("%d",c[1][chg[i]]);
        if(i<n)
            printf(" ");
        else
            puts("");
    }
    for(int i=1;i<n;i++){
        int cur=e[i][0];
        if(dep[e[i][0]]<dep[e[i][1]])
            cur=e[i][1];
        printf("%d",c[0][chg[cur]]);
        if(i<n-1)
            printf(" ");
    }
    puts("");
    return ;
}
时间: 2024-10-25 13:31:07

HDU 5044 Tree 树链剖分的相关文章

HDU 5044 (树链剖分+树状数组+点/边改查)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变态树链剖分模板题.将以往树链剖分的点&边修改和查询合在一起之后,难度上去不少. 第一个卡人点是读入优化. 第二个卡人点是树状数组.由于要查询所有点,如果使用线段树,每次都要扫到底层才能取出点值,必T无疑. 然后使用树状数组之后,树链剖分的点/边修改写法有些变动. 点查询变化不大. 边查询只要查询一下

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

SPOJ375 Query on a tree 树链剖分

SPOJ375  Query on a tree   树链剖分 no tags You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

SPOJ - QTREE 375 Query on a tree 树链剖分+线段树

操作1:修改第k条边权. 操作2:询问两点间最大边权. 树链剖分,然后线段树维护最大值 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #inclu

HDU 3966 RE 树链剖分 Aragorn&#39;s Story

给一棵点带权的图 有这样一个操作: 使树上某一条路径所有点权值增减 每次询问某个点现在的权值. 树链剖分完以后,就是线段树的成段更新了. 这题感觉A不了了,无限RE,手动开栈也没卵用. 还是把我辛辛苦苦写的代码贴一下吧. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using

hdu 4912 Paths on the tree(树链剖分+贪心)

题目链接:hdu 4912 Paths on the tree 题目大意:给定一棵树,和若干个通道,要求尽量选出多的通道,并且两两通道不想交. 解题思路:用树链剖分求LCA,然后根据通道两端节点的LCA深度排序,从深度最大优先选,判断两个节点均没被标 记即为可选通道.每次选完通道,将该通道LCA以下点全部标记. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

spoj 375 Query on a tree (树链剖分)

Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to ti or Q

SPOJ Query on a tree 树链剖分 水题

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to tior QUERY a b : ask fo