[模板]树链剖分

原题链接:https://www.luogu.org/problemnew/show/P3384

树链剖分+线段树,备用。

等待补充详细解释中。

/*
1 x y z  x到y最短路径上加上z
2 x y 求x到y的最短路径上的节点值之和
3 x z 以x为根节点的子树内的所有节点值都加上z
4 x 以x为根节点的所有节点值之和
*/
#include<cstdio>
void read(int &y)
{
    y=0;char x=getchar();int f=1;
    while(x<‘0‘||x>‘9‘)
    {
        if(x==‘-‘) f=-1;
        x=getchar();
    }
    while(x>=‘0‘&&x<=‘9‘)
    {
        y=y*10+x-‘0‘;
        x=getchar();
    }
    y*=f;
}
struct edge
{
    int u,v;
}e[200005];
int n,m,s,p,cnt,maxson,tot;
int head[100005];
int dep[100005],fa[100005],siz[100005];
int id[100005],wt[100005],w[100005];
int son[100005],top[100005];
int a[400005],lz[400005];
long long res;
void swap(int &a,int &b)
{
    int t=a;
    a=b;
    b=t;
}
void add(int u,int v)
{
    e[++cnt].u=head[u];
    e[cnt].v=v;
    head[u]=cnt;
}
void dfs1(int x,int f,int deep)
{
    dep[x]=deep;
    fa[x]=f;
    siz[x]=1;
    int maxson=-1;
    for(int i=head[x];i;i=e[i].u)
    {
        int t=e[i].v;
        if(t==f) continue;
        dfs1(t,x,deep+1);
        siz[x]+=siz[t];
        if(siz[t]>maxson)
        {
            son[x]=t;
            maxson=siz[t];
        }
    }
}
void dfs2(int x,int topf)
{
    id[x]=++tot;
    wt[tot]=w[x];
    top[x]=topf;
    if(!son[x]) return;
    dfs2(son[x],topf);
    for(int i=head[x];i;i=e[i].u)
    {
        int t=e[i].v;
        if(t==fa[x]||t==son[x]) continue;
        dfs2(t,t);
    }
}
void build(int o,int l,int r)
{
    if(l==r)
    {
        a[o]=wt[l]%p;
        return;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    a[o]=(a[o<<1]+a[o<<1|1])%p;
}
void putdown(int o,int l,int r)
{
    lz[o<<1]+=lz[o];
    lz[o<<1|1]+=lz[o];
    int mid=(l+r)>>1;
    a[o<<1]+=lz[o]*(mid-l+1);a[o<<1]%=p;
    a[o<<1|1]+=lz[o]*(r-mid);a[o<<1|1]%=p;
    lz[o]=0;
}
void query(int ll,int rr,int o,int l,int r)
{
    if(ll<=l&&rr>=r)
    {
        res=(res+a[o])%p;
        return;
    }
    if(lz[o]) putdown(o,l,r);
    int mid=(l+r)>>1;
    if(ll<=mid) query(ll,rr,o<<1,l,mid);
    if(rr>mid) query(ll,rr,o<<1|1,mid+1,r);
}
void update(int k,int ll,int rr,int o,int l,int r)
{
    if(ll<=l&&rr>=r)
    {
        lz[o]+=k;
        a[o]=(a[o]+k*(r-l+1))%p;
    }
    else
    {
        if(lz[o]) putdown(o,l,r);
        int mid=(l+r)>>1;
        if(ll<=mid) update(k,ll,rr,o<<1,l,mid);
        if(rr>mid) update(k,ll,rr,o<<1|1,mid+1,r);
        a[o]=(a[o<<1]+a[o<<1|1])%p;
    }
}
int qy(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        res=0;
        query(id[top[x]],id[x],1,1,n);
        ans=(ans+res)%p;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    res=0;
    query(id[x],id[y],1,1,n);
    ans+=res;
    return ans%p;
}
void upy(int x,int y,int k)
{
    k%=p;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        update(k,id[top[x]],id[x],1,1,n);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    update(k,id[x],id[y],1,1,n);
}
int qson(int x)
{
    res=0;
    query(id[x],id[x]+siz[x]-1,1,1,n);
    return res;
}
void upd(int x,int k)
{
    k%=p;
    update(k,id[x],id[x]+siz[x]-1,1,1,n);
}
int main()
{
    int op,x,y,z;
    read(n);read(m);read(s);read(p);
    for(int i=1;i<=n;i++) read(w[i]);
    for(int i=1;i<n;i++)
    {
        read(x);read(y);
        add(x,y);
        add(y,x);
    }
    dfs1(s,0,1);
    dfs2(s,s);
    build(1,1,n);
    while(m--)
    {
        read(op);
        if(op==1)
        {
            read(x);read(y);read(z);
            upy(x,y,z);
        }
        else if(op==2)
        {
            read(x);read(y);
            printf("%d\n",qy(x,y));
        }
        else if(op==3)
        {
            read(x);read(z);
            upd(x,z);
        }
        else
        {
            read(x);
            printf("%d\n",qson(x));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zeroform/p/8342011.html

时间: 2024-10-07 18:24:24

[模板]树链剖分的相关文章

[luogu P3384] [模板]树链剖分

[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数

luoguP3384 [模板]树链剖分

luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #define rg register

模板 树链剖分BFS版本

//点和线段树都从1开始 //边使用vector vector<int> G[maxn]; int dfs_clock,que[maxn*2],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn],deep[maxn],fa[maxn],idx[maxn]; //采用静态链表 //a[i] 是初始时树上每个点的权值 //b[i] 是经过bfs后每个点的权值 //idx[i] 是每个点在全局线段树中的下标 void build_List() { int ft

模板——树链剖分

放个板子而已.. #include<stdio.h> #include<algorithm> #define ls root<<1 #define rs root<<1|1 using namespace std; typedef long long ll; int n,m,r;ll res,p; const int MAXN = 100005; ll a[MAXN]; struct edge{ int v,nxt; }e[MAXN<<1]; s

[模板] 树链剖分找LCA

#include <cstdio> #include <cstring> #define MAX 500005 int d[MAX],fa[MAX],size[MAX],top[MAX],son[MAX]; int N,M,S,tot=0; int head[MAX]; struct edge{ int v,next; }G[MAX<<1]; inline void add(int u,int v){ G[++tot].v=v;G[tot].next=head[u];h

树链剖分[模板](洛谷 P3384)

洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 如果这三个知识点没掌握好的话,树链剖分难以理解也是当然的. 树链剖分 树链剖分 就是对一棵树分成几条链,把树形变为线性,减少处理难度 概念 dfs1() dfs2() 对剖过后的树建线段树 处理问题 概念 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子 叶子节点没有重儿子

Hdu5044Tree 树链剖分

输入输出挂,扩栈挂,模板树链剖分. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<stdlib.h> #include<algorithm> using namespace std; const int maxn = 111111; struct Node { int next; i

树链剖分 模板

关于树链剖分 模板: 1 const int MAXN = 20010; 2 struct Edge 3 { 4 int to,next; 5 } edge[MAXN*2]; 6 int head[MAXN],tot; 7 8 int top[MAXN];//top[v]表示v所在的重链的顶端节点 9 int fa[MAXN]; //父亲节点 10 int deep[MAXN];//深度 11 int num[MAXN];//num[v]表示以v为根的子树的节点数 12 int p[MAXN];

luogu3384 【模板】树链剖分

P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,