luoguP3384 [模板]树链剖分

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

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<ctime>
#include<queue>
#define rg register
#define lst long long
#define N 100050
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;

int n,m,root,p,cnt,ss,ans;
struct EDGE{
    int to,nxt;
}edge[N<<1];
struct TREE{
    int l,r,siz,sum,lazy;
}ljl[N<<2];
int first[N],v[N];
int fa[N],deep[N],son[N],size[N],top[N],num[N],vv[N];

inline int read()
{
    rg int s=0,m=1;rg char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar();
    if(ch==‘-‘)m=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)s=(s<<3)+(s<<1)+ch-‘0‘,ch=getchar();
    return s*m;
}

inline void add(rg int p,rg int q){edge[++cnt]=(EDGE){q,first[p]};first[p]=cnt;}

void init()//输入点权和加边
{
    n=read(),m=read(),root=read(),p=read();
    for(rg int i=1;i<=n;++i)v[i]=read(),size[i]=1;
    for(rg int i=1;i<n;++i)
    {
        rg int p=read(),q=read();
        add(p,q),add(q,p);
    }
}
//______________________________________________输入,加边
void dfs_1(rg int now,rg int dep,rg int fm)//Dfs预处理 父亲节点 深度 子树大小
{
    rg int kk=0;//辅助找重儿子
    fa[now]=fm,deep[now]=dep;//父亲,深度
    for(rg int i=first[now];i;i=edge[i].nxt)//枚举儿子节点(边)
    {
        rg int qw=edge[i].to;//儿子
        if(qw==fm)continue;
        dfs_1(qw,dep+1,now);//继续去找
        size[now]+=size[qw];//处理子树大小
        if(size[qw]>kk)kk=size[qw],son[now]=qw;//找重儿子
    }
}

void dfs_2(rg int now,rg int up)//找新的dfs序vv 处理now在dfs序中的位置num 所在重链的顶端
{
    num[now]=++ss,top[now]=up,vv[ss]=v[now];//如上
    if(son[now])dfs_2(son[now],top[now]);//先递归找重儿子
    for(rg int i=first[now];i;i=edge[i].nxt)//再找其他儿子(边)
    {
        rg int qw=edge[i].to;
        if(qw!=fa[now]&&qw!=son[now])//如上
            dfs_2(qw,qw);
    }
}
//__________________________________________________________dfs预处理
inline void Pushup(rg int now)//处理一下和
{
    ljl[now].sum=(ljl[ls].sum+ljl[rs].sum+p)%p;
}

inline void Pushdown(rg int now)//lazy标记下放
{
    if(ljl[now].lazy)
    {
        ljl[ls].sum+=ljl[ls].siz*ljl[now].lazy;ljl[ls].sum%=p;
        ljl[rs].sum+=ljl[rs].siz*ljl[now].lazy;ljl[rs].sum%=p;
        ljl[ls].lazy+=ljl[now].lazy;ljl[ls].lazy%=p;
        ljl[rs].lazy+=ljl[now].lazy;ljl[rs].lazy%=p;
        ljl[now].lazy=0;
    }
}

void build(rg int now,rg int ll,rg int rr)//建线段树
{
    ljl[now]=(TREE){ll,rr,rr-ll+1};
    if(ll==rr){ljl[now].sum=vv[ll];return;}
    rg int mid=(ll+rr)>>1;
    build(ls,ll,mid),build(rs,mid+1,rr);
    Pushup(now);
}

void update(rg int now,rg int ll,rg int rr ,rg int xx)//区间ll到rr上加一个xx
{
    if(ljl[now].l>=ll&&ljl[now].r<=rr)
    {
        ljl[now].sum+=ljl[now].siz*xx;
        ljl[now].lazy+=xx;return;
    }
    Pushdown(now);
    rg int mid=(ljl[now].l+ljl[now].r)/2;
    if(rr<=mid)update(ls,ll,rr,xx);
    if(ll>mid)update(rs,ll,rr,xx);
    if(ll<=mid&&rr>mid)
        update(ls,ll,mid,xx),update(rs,mid+1,rr,xx);
    Pushup(now);
}

int Query(rg int now,rg int ll,rg int rr)//求ll到rr区间的和
{
    rg int s=0;
    if(ljl[now].l>=ll&&ljl[now].r<=rr)return ljl[now].sum;
    Pushdown(now);
    rg int mid=(ljl[now].l+ljl[now].r)>>1;
    if(rr<=mid)s+=Query(ls,ll,rr);
    if(ll>mid) s+=Query(rs,ll,rr);
    if(ll<=mid&&rr>mid)
        s+=Query(ls,ll,mid)+Query(rs,mid+1,rr);
    Pushup(now),s%=p;
    return s;
}
//__________________________________________________________线段树
inline void tree_work(rg int x,rg int y,rg int z,rg int op)
{
    if(num[x]>num[y])swap(x,y);
    if(!op)update(1,num[x],num[y],z);
    else ans+=Query(1,num[x],num[y]);ans%=p;
}

inline void tree_Work(rg int x,rg int y,rg int z,rg int op)
{
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        tree_work(top[x],x,z,op);
        x=fa[top[x]];
    }
    tree_work(x,y,z,op);
}

inline void ANS()
{
    for(rg int i=1;i<=m;++i)
    {
        rg int type=read();ans=0;
        if(type==1){rg int x=read(),y=read(),z=read();          tree_Work(x,y,z,0);}
        if(type==2){rg int x=read(),y=read();                   tree_Work(x,y,0,1);}
        if(type==3){rg int x=read(),z=read();  update(1,num[x],num[x]+size[x]-1,z);}
        if(type==4){rg int x=read();          ans=Query(1,num[x],num[x]+size[x]-1);}
        if(type==2||type==4)printf("%d\n",ans);
    }
}

int main()
{
    init();
    dfs_1(root,0,0);
    dfs_2(root,root);
    build(1,1,ss);
    ANS();
    return 0;
}

原文地址:https://www.cnblogs.com/cjoierljl/p/9107499.html

时间: 2024-07-30 22:10:38

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

[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个正整数

[模板]树链剖分

原题链接: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<

模板 树链剖分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

luoguP3384 【模板】树链剖分

树链剖分模板题,学习树链剖分推荐ppt https://wenku.baidu.com/view/a088de01eff9aef8941e06c3.html?from=search #include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; typedef long long ll; int dep[maxn],a[maxn],size[maxn],top[maxn],son[maxn],pos[maxn],f[max

树链剖分 模板

关于树链剖分 模板: 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];