hdu 5044 Tree (树链剖分+打标记)

HDU 5044 Tree:http://acm.hdu.edu.cn/showproblem.php?pid=5044

题意:给你一棵树,定义两种操作 1.将节点a,b路径上的每个节点都加上一个值k     2.将节点a,b路径上的每条边都加上一个值k

最后输出每个节点的权值和每条边的权值。

思路:此题目算是我树链剖分的第一题了,去kuangbin大神的博客上取的模板。如果是最朴素的操作每次操作的复杂度都是o(n),复杂度肯定是不够的,而树链剖分就可以将这个o(n)的复杂度降到o(logn),剩下的处理就是线性的处理了。

线性的处理方法就是打标记,以操作一为例,我们可以将节点a上打一个k的标记,在节点b+1上打一个-k的标记,当所有操作处理完之后从头到尾扫一遍即可求出最后的结果。


code:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
//树链剖分 很容易爆栈 此代码是C++的手动扩栈 G++可能不爆栈但是会T的
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;

const int maxn=105000;
const int maxe=400010;

struct edge
{
    int to,next;
} P[maxe];

int head[maxn],si;
int top[maxn];
int fa[maxn];
int deep[maxn];
int num[maxn];
int p[maxn];
int fp[maxn];
int son[maxn];
int pos;

typedef long long LL;

void init()
{
    si=0;
    memset(head,-1,sizeof(head));
    pos=1;
    memset(son,-1,sizeof(son));
}
void add_edge(int s,int t)
{
    P[si].to=t;
    P[si].next=head[s];
    head[s]=si++;
}

void dfs1(int u,int pre,int d)
{
    deep[u]=d;
    fa[u]=pre;
    num[u]=1;
    for(int i=head[u];i!=-1;i=P[i].next){
        int v=P[i].to;
        if(v!=pre){
            dfs1(v,u,d+1);
            num[u]+=num[v];
            if(son[u]==-1||num[v]>num[son[u]]) son[u]=v;
        }
    }
}

void getpos(int u,int sp)
{
    top[u]=sp;
    p[u]=pos++;
    fp[p[u]]=u;
    if(son[u]==-1) return ;
    getpos(son[u],sp);
    for(int i=head[u];i!=-1;i=P[i].next){
        int v=P[i].to;
        if(v!=son[u]&&v!=fa[u]) getpos(v,v);
    }
}

long long w1[maxn];
void add1(int u,int v,int w)
{
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        w1[p[f1]]+=w;
        w1[p[u]+1]-=w;
        u=fa[f1];
        f1=top[u];
    }
    if(deep[u]>deep[v]) swap(u,v);
    w1[p[u]]+=w;
    w1[p[v]+1]-=w;
}
long long w2[maxn];
void add2(int u,int v,int w){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        w2[p[f1]]+=w;
        w2[p[u]+1]-=w;
        u=fa[f1];
        f1=top[u];
    }
    if(u==v) return ;
    if(deep[u]>deep[v]) swap(u,v);
    w2[p[son[u]]]+=w;
    w2[p[v]+1]-=w;
}
pair<int,int> pp[maxn];
int link[maxn];
LL ans1[maxn],ans2[maxn];
int main()
{
    int T,nn,mm,s,t,w;
    char cc[10];
    scanf("%d",&T);
    for(int kk=1;kk<=T;kk++){
        scanf("%d%d",&nn,&mm);
        init();
        for(int i=0;i<nn-1;i++){
            scanf("%d%d",&s,&t);
            add_edge(s,t);
            add_edge(t,s);
            pp[i]=make_pair(s,t);
        }
        dfs1(1,0,0);
        getpos(1,1);
        memset(w1,0,sizeof(w1));
        memset(w2,0,sizeof(w2));
        while(mm--){
            scanf("%s",cc);
            scanf("%d%d%d",&s,&t,&w);
            if(cc[3]=='1') add1(s,t,w);
            else           add2(s,t,w);
        }
        //每条边都存在深度较大的那个节点上
        for(int i=0;i<nn-1;i++){
            if(deep[pp[i].first]>deep[pp[i].second]) swap(pp[i].first,pp[i].second);
            link[pp[i].second]=i;
        }
        for(int i=1;i<=nn;i++){
            w1[i]+=w1[i-1];
            w2[i]+=w2[i-1];
            ans1[fp[i]]=w1[i];
            ans2[link[fp[i]]]=w2[i];
        }
        printf("Case #%d:\n",kk);
        for(int i=1;i<=nn;i++){
            printf("%I64d%c",ans1[i],i==nn? '\n':' ');
        }
        for(int i=0;i<nn-1;i++){
            printf("%I64d",ans2[i]);
            if(i!=nn-2) printf(" ");
        }
        printf("\n");
    }
    return 0;
}

这个算是我第一道树链剖分的题目了


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值