树链剖分模板

Posted hhhen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分模板相关的知识,希望对你有一定的参考价值。

 

题目链接:https://www.luogu.com.cn/problem/P3178

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

  • 操作 1 :把某个节点 x 的点权增加 a 。
  • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
  • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入格式

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

输出格式

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例

输入 #1
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
输出 #1
6
9
13

说明/提示

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+9;
const double ep=1e-6;
const int mod=998244353;
const int MAX=1e9;
#define mk make_pair
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define pb push_back
typedef long long ll;
ll n,m,b[maxn],size[maxn],d[maxn];
ll f[maxn][20];
vector<int>vec[maxn];
ll be[maxn];
ll pp[maxn],cnt;
struct node{
    ll l,r,sum,lazy;
}a[maxn<<2];
void dfs(int x,int p)
{
    size[x]=1;
    for(int j:vec[x])
    {
        if(j==p)continue;
        f[j][0]=x;
        d[j]=d[x]+1;
        dfs(j,x);
        size[x]+=size[j];
    }
}
void dfs1(int x,int chain,int p)
{
    be[x]=chain;
    pp[x]=++cnt;
    int pos=0;
    for(int j:vec[x])
    {
        if(j==p)continue;
        if(size[j]>size[pos])pos=j;
    }
    if(!pos)return;
    dfs1(pos,chain,x);
    for(int j:vec[x])
    {
        if(j==p||j==pos)continue;
        dfs1(j,j,x);
    }
}

int lca(int a,int b)
{
    if(d[a]<d[b])swap(a,b);
    for(int i=17;i>=0;i--)if((d[a]-d[b])>>i)a=f[a][i];
    if(a==b)return a;
    for(int i=17;i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
    return f[a][0];
}
void build(int k,int l,int r)
{
    a[k].l=l,a[k].r=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
void pushup(int k)
{
    a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void pushdown(int k)
{
    if(a[k].l==a[k].r)
    {
        a[k].lazy=0;return;
    }
    a[k<<1].sum+=(a[k<<1].r-a[k<<1].l+1)*a[k].lazy;
    a[k<<1|1].sum+=(a[k<<1|1].r-a[k<<1|1].l+1)*a[k].lazy;
    a[k<<1].lazy+=a[k].lazy;
    a[k<<1|1].lazy+=a[k].lazy;
    a[k].lazy=0; 
}
void change(int k,int l,int r,int x)
{
    if(a[k].l>=l&&a[k].r<=r)
    {
        a[k].sum+=(a[k].r-a[k].l+1)*x;
        a[k].lazy+=x;
        return;
    }
    if(a[k].lazy)pushdown(k);
    int mid=(a[k].l+a[k].r)>>1;
    if(l<=mid)change(k<<1,l,r,x);
    if(r>mid)change(k<<1|1,l,r,x);
    pushup(k);
}
ll query_SUM(int k,int l,int r)
{
    if(a[k].lazy)pushdown(k);
    if(a[k].l>=l&&a[k].r<=r)
    {
        return a[k].sum;
    }
    int mid=(a[k].l+a[k].r)>>1;
    if(r<=mid)return query_SUM(k<<1,l,r);
    else if(l>mid)return query_SUM(k<<1|1,l,r);
    else return query_SUM(k<<1,l,mid)+query_SUM(k<<1|1,mid+1,r);
}
ll query(int x)
{
    ll sum1=0;
    while(be[x]!=1)
    {
        sum1+=query_SUM(1,pp[be[x]],pp[x]);
        x=f[be[x]][0];
    }
    sum1+=query_SUM(1,1,pp[x]);
    return sum1;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        vec[u].pb(v);
        vec[v].pb(u);
    }
    dfs(1,0);
    for(int i=1;i<=17;i++)for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1];
    dfs1(1,1,-1);
    build(1,1,n);
    for(int i=1;i<=n;i++)change(1,pp[i],pp[i],b[i]);
    
    while(m--)
    {
        int op,x,y;
        scanf("%d%d",&op,&x);
        if(op==1)
        {
            scanf("%d",&y);
            change(1,pp[x],pp[x],y);
        }
        if(op==2)
        {
            scanf("%d",&y);
            change(1,pp[x],pp[x]+size[x]-1,y);
        }
        if(op==3)
        {
            printf("%lld
",query(x));
        }
    }
}

 

以上是关于树链剖分模板的主要内容,如果未能解决你的问题,请参考以下文章

模板时间◆模板·II◆ 树链剖分

树链剖分模板题(luogu3384 模板树链剖分)

树链剖分模板

BZOJ 2243--染色(树链剖分)

模板树链剖分

luoguP3384 模板树链剖分