2014 ACM/ICPC Asia Regional Shanghai Online C - Tree —— 树上前缀和

This way

题意:

给你一棵树,两种操作:
1.将x到y的路径上的所有点的权值+k
2.将x到y路径上的所有边的权值+k
所有操作结束后问你所有的点权和所有的边权

题解:

这道题用树链剖分估计会T,因为它是 n l o g 2 n nlog^2n nlog2n的。
由于每次加的一定是一条链或者两条链,那么我们只需要用前缀和的思想去做即可,也就是说加点权的时候在x的位置加上k,y的位置+k,lca的位置-k,因为x和y的值合并了之后就变成2k,需要减掉一个。然后在lca的父亲位置-k。
边权的话只需要在x,y的位置+k,lca的位置-2k即可。我们将边权当做儿子节点处理,并且这是普遍解决边权存储位置的方法。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
ll sum_p[N],sum_e[N],flag[N];
struct node
{
    int to,next;
}e[N*2];
int cnt,head[N];
void add(int x,int y)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
int fa[N][25],dep[N];
void dfs(int x,int f)
{
    fa[x][0]=f;
    dep[x]=dep[f]+1;
    for(int i=head[x];~i;i=e[i].next)
    {
        int ne=e[i].to;
        if(ne==f)
            continue;
        dfs(ne,x);
    }
}
int n,m;
void deal()
{
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int xx[N],yy[N];
void dfs_p(int x,int f)
{
    for(int i=head[x];~i;i=e[i].next)
    {
        int ne=e[i].to;
        if(ne==f)
            continue;
        dfs_p(ne,x);
        sum_p[x]+=sum_p[ne]-flag[ne];
    }
}
void dfs_e(int x,int f)
{
    for(int i=head[x];~i;i=e[i].next)
    {
        int ne=e[i].to;
        if(ne==f)
            continue;
        dfs_e(ne,x);
        sum_e[x]+=sum_e[ne];
    }
}
int main()
{
    int t,cas=0;
    scanf("%d",&t);
    while(t--)
    {
        int x,y;
        ll k;
        char op[10];
        scanf("%d%d",&n,&m);
        cnt=0;
        for(int i=1;i<=n;i++)
            sum_p[i]=sum_e[i]=flag[i]=0,head[i]=-1;
        for(int i=1;i<n;i++)
            scanf("%d%d",&xx[i],&yy[i]),add(xx[i],yy[i]),add(yy[i],xx[i]);
        dfs(1,0),deal();
        while(m--)
        {
            scanf("%s%d%d%lld",op,&x,&y,&k);
            int l=lca(x,y);
            if(op[3]=='1')
                sum_p[x]+=k,sum_p[y]+=k,sum_p[l]-=k,flag[l]+=k;
            else
                sum_e[x]+=k,sum_e[y]+=k,sum_e[l]-=2*k;
        }
        dfs_p(1,0),dfs_e(1,0);
        printf("Case #%d:\n",++cas);
        for(int i=1;i<=n;i++)
        {
            printf("%lld",sum_p[i]);
            if(i==n)
                printf("\n");
            else
                printf(" ");
        }
        if(n==1)
            printf("\n");
        for(int i=1;i<n;i++)
        {
            printf("%lld",dep[xx[i]]>dep[yy[i]]?sum_e[xx[i]]:sum_e[yy[i]]);
            if(i==n-1)
                printf("\n");
            else
                printf(" ");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值