【HAOI2015】【BZOJ4034】树上操作T2

Description

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input

第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操
作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
Output

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample Input
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
Sample Output
6
9
13
HINT

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不

会超过 10^6 。

Source

鸣谢bhiaibogf提供

太久不写链剖线段树手已经生了..WA了好多次
跑的不算快(还是常数大吖..)
跟NOI2015 D1T2一个姿势

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100100
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
#define GET (ch>='0'&&ch<='9')
#define LL long long
using namespace std;
int n,m,top,Top;
bool vis[MAXN];
int val[MAXN],chain[MAXN],bot[MAXN],num[MAXN],size[MAXN],fa[MAXN][18],deep[MAXN];
struct edge
{
    int to;
    edge *next;
}e[MAXN<<1],*prev[MAXN];
struct seg
{
    int l,r;
    LL sum,add;
}tree[MAXN<<2];
void push_up(int rt)
{
    tree[rt].sum=tree[ln].sum+tree[rn].sum;
}
void build(int rt=1,int l=1,int r=n)
{
    tree[rt].l=l;tree[rt].r=r;int mid=(l+r)>>1;
    if (l==r)   return;
    build(lchild);build(rchild);
}
void push_down(int rt)
{
    if (tree[rt].l==tree[rt].r) return;
    if (tree[rt].add!=0)
    {
        tree[ln].add+=tree[rt].add;tree[rn].add+=tree[rt].add;
        tree[ln].sum+=(LL)(tree[ln].r-tree[ln].l+1)*tree[rt].add;tree[rn].sum+=(LL)(tree[rn].r-tree[rn].l+1)*tree[rt].add;
        tree[rt].add=0;
    }
}
void modify(int rt,int l,int r,int delta)
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
    if (l<=L&&R<=r)
    {
        tree[rt].sum+=(LL)(R-L+1)*delta;tree[rt].add+=delta;
        return;
    }
    if (r<=mid) modify(ln,l,r,delta);
    else
    if (l>mid)  modify(rn,l,r,delta);
    else    modify(ln,l,mid,delta),modify(rn,mid+1,r,delta);
    push_up(rt);
}
LL query(int rt,int l,int r)
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
    if (l<=L&&R<=r) return tree[rt].sum;
    if (r<=mid) return query(ln,l,r);
    if (l>mid)  return query(rn,l,r);
    return query(ln,l,mid)+query(rn,mid+1,r);
}
LL Query(int a,int b)
{
    LL ret=0;
    while (chain[a]!=chain[b])
    {
        ret+=query(1,num[chain[a]],num[a]);
        a=fa[chain[a]][0];
    }
    ret+=query(1,num[b],num[a]);
    return ret;
}
void insert(int u,int v)
{
    e[++top].to=v;e[top].next=prev[u];prev[u]=&e[top];
}
void in(int &x)
{
    int flag=1;char ch=getchar();x=0;
    while (!GET)    flag=ch=='-'?-1:flag,ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();x*=flag;
}
void dfs1(int x)
{
    size[x]=1;vis[x]=1;
    for (int i=1;i<=17;i++)
        if ((1<<i)<=deep[x])    fa[x][i]=fa[fa[x][i-1]][i-1];
        else    break;
    for (edge *i=prev[x];i;i=i->next)
        if (!vis[i->to])    deep[i->to]=deep[x]+1,fa[i->to][0]=x,dfs1(i->to),size[x]+=size[i->to];
}
void dfs2(int x,int last)
{
    int t=0;
    chain[x]=last;num[x]=++Top;bot[x]=Top;
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&size[i->to]>size[t])   t=i->to;
    if (!t) return;dfs2(t,last);
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&i->to!=t)  dfs2(i->to,i->to);
    bot[x]=Top;
}
int main()
{
    in(n);in(m);int opt,u,v;build();
    for (int i=1;i<=n;i++)  in(val[i]),bot[i]=i;
    for (int i=1;i<n;i++)   in(u),in(v),insert(u,v),insert(v,u);
    dfs1(1);dfs2(1,1);
    for (int i=1;i<=n;i++)  modify(1,num[i],num[i],val[i]);
    while (m--)
    {
        in(opt);
        if (opt==1) in(u),in(v),modify(1,num[u],num[u],v);
        if (opt==2) in(u),in(v),modify(1,num[u],bot[u],v);
        if (opt==3) in(u),printf("%lld\n",Query(u,1));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值