zoukankan      html  css  js  c++  java
  • 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释

    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,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

    接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。

    接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)

    接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:

    操作1: 1 x y z

    操作2: 2 x y

    操作3: 3 x z

    操作4: 4 x

    输出格式:

    输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)

    输入输出样例

    输入样例#1: 复制
    5 5 2 24
    7 3 7 8 0 
    1 2
    1 5
    3 1
    4 1
    3 4 2
    3 2 2
    4 5
    1 5 1 3
    2 1 3
    输出样例#1: 复制
    2
    21

    说明

    时空限制:1s,128M

    数据规模:

    对于30%的数据: N leq 10, M leq 10N10,M10

    对于70%的数据: N leq {10}^3, M leq {10}^3N103,M103

    对于100%的数据: N leq {10}^5, M leq {10}^5N105,M105

    ( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )

    样例说明:

    树的结构如下:

    各个操作如下:

    故输出应依次为2、21(重要的事情说三遍:记得取模)

    题意很直接,直接模板题。

    写了两天,最后发现,加边时add(v,u)的括号写成[ ]了,可真是捞啊。

    写了注释。

    代码:

      1 //洛谷-P3384 【模板】树链剖分-树链剖分
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<cstring>
      5 #include<algorithm>
      6 #include<bitset>
      7 #include<cassert>
      8 #include<cctype>
      9 #include<cmath>
     10 #include<cstdlib>
     11 #include<ctime>
     12 #include<deque>
     13 #include<iomanip>
     14 #include<list>
     15 #include<map>
     16 #include<queue>
     17 #include<set>
     18 #include<stack>
     19 #include<vector>
     20 using namespace std;
     21 typedef long long ll;
     22 
     23 const double PI=acos(-1.0);
     24 const double eps=1e-6;
     25 //const ll mod=1e9+7;
     26 const int inf=0x3f3f3f3f;
     27 const int maxn=2e5+10;
     28 const int maxm=100+10;
     29 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
     30 #define lson l,m,rt<<1
     31 #define rson m+1,r,rt<<1|1
     32 
     33 int sum[maxn<<2],lazy[maxn<<2];
     34 int n,m,r,mod;
     35 int head[maxn],tot;
     36 
     37 int son[maxn],tid[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
     38 int w[maxn],wt[maxn];
     39 
     40 struct Edge{
     41     int to,next;
     42 }edge[maxn];
     43 
     44 void add(int u,int v)//链式前向星存边
     45 {
     46     edge[tot].to=v;
     47     edge[tot].next=head[u];
     48     head[u]=tot++;
     49 }
     50 
     51 void init()//初始化
     52 {
     53     memset(head,-1,sizeof(head));
     54     tot=0;cnt=0;
     55 }
     56 
     57 //线段树部分
     58 void pushup(int rt)//上传lazy标记
     59 {
     60     sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
     61 }
     62 
     63 void pushdown(int rt,int m)//下放lazy标记
     64 {
     65     if(lazy[rt]){
     66         lazy[rt<<1]+=lazy[rt];
     67         lazy[rt<<1|1]+=lazy[rt];
     68         sum[rt<<1]+=(m-(m>>1))*lazy[rt],sum[rt<<1]%=mod;
     69         sum[rt<<1|1]+=(m>>1)*lazy[rt],sum[rt<<1|1]%=mod;
     70         lazy[rt]=0;
     71     }
     72 }
     73 
     74 void build(int l,int r,int rt)
     75 {
     76     lazy[rt]=0;
     77     if(l==r){
     78         sum[rt]=wt[l],sum[rt]%=mod;//新的编号点权
     79         return ;
     80     }
     81 
     82     int m=(l+r)>>1;
     83     build(lson);
     84     build(rson);
     85     pushup(rt);
     86 }
     87 
     88 void update(int L,int R,int c,int l,int r,int rt)//区间更新
     89 {
     90     if(L<=l&&r<=R){
     91         lazy[rt]+=c;
     92         sum[rt]+=c*(r-l+1),sum[rt]%=mod;
     93         return ;
     94     }
     95 
     96     pushdown(rt,r-l+1);
     97     int m=(l+r)>>1;
     98     if(L<=m) update(L,R,c,lson);
     99     if(R> m) update(L,R,c,rson);
    100     pushup(rt);
    101 }
    102 
    103 int query(int L,int R,int l,int r,int rt)
    104 {
    105     if(L<=l&&r<=R){
    106         return sum[rt];
    107     }
    108 
    109     int ret=0;
    110     pushdown(rt,r-l+1);
    111     int m=(l+r)>>1;
    112     if(L<=m) ret+=query(L,R,lson),ret%=mod;
    113     if(R> m) ret+=query(L,R,rson),ret%=mod;
    114     return ret;
    115 }
    116 
    117 //树链剖分部分
    118 void dfs1(int u,int father)
    119 {
    120     siz[u]=1;//记录每个节点子树大小
    121     fa[u]=father;//标记节点的父亲
    122     dep[u]=dep[father]+1;//标记深度
    123     for(int i=head[u];~i;i=edge[i].next){
    124         int v=edge[i].to;
    125         if(v==father) continue;//如果连接的是当前节点的父亲节点,则不处理
    126         dfs1(v,u);
    127         siz[u]+=siz[v];//直接子树节点相加,当前节点的size加上子节点的size
    128         if(siz[v]>siz[son[u]]){//如果没有设置过重节点son或者子节点v的size大于之前记录的重节点son,进行更新,保存重儿子
    129             son[u]=v;//标记u的重儿子为v
    130         }
    131     }
    132 }
    133 
    134 void dfs2(int u,int tp)
    135 {
    136     top[u]=tp;//标记每个重链的顶端
    137     tid[u]=++cnt;//每个节点剖分以后的新编号(dfs的执行顺序)
    138     wt[cnt]=w[u];//新编号的对应权值
    139     if(!son[u]) return ;//如果当前节点没有处在重链上,则不处理,否则就将这条重链上的所有节点都设置成起始的重节点
    140     dfs2(son[u],tp);//搜索下一个重儿子
    141     for(int i=head[u];~i;i=edge[i].next){
    142         int v=edge[i].to;
    143         if(v==fa[u]||v==son[u]) continue;//处理轻儿子,如果连接节点不是当前节点的重节点并且也不是u的父节点,则将其的top设置成自己,进一步递归
    144         dfs2(v,v);//每一个轻儿子都有一个从自己开始的链
    145     }
    146 }
    147 
    148 void update_path(int x,int y,int k)//路径更新
    149 {
    150     k%=mod;
    151     while(top[x]!=top[y]){
    152         if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大
    153         update(tid[top[x]],tid[x],k,1,n,1);
    154         x=fa[top[x]];
    155     }
    156 
    157     if(dep[x]>dep[y]) swap(x,y);//使x深度较小
    158     update(tid[x],tid[y],k,1,n,1);
    159 }
    160 
    161 int getsum_path(int x,int y)//路径求和
    162 {
    163     int ans=0;
    164     while(top[x]!=top[y]){//当两个点不在同一条链上
    165         if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x深度较大
    166         ans+=query(tid[top[x]],tid[x],1,n,1),ans%=mod;
    167         x=fa[top[x]];//x跳到x所在链顶端的这个点的上面一个点
    168     }
    169 
    170     if(dep[x]>dep[y]) swap(x,y);//当两个点处于同一条链,使x深度较小
    171     ans+=query(tid[x],tid[y],1,n,1),ans%=mod;
    172     return ans;
    173 }
    174 
    175 void update_subtree(int x,int k)//子树更新
    176 {
    177     update(tid[x],tid[x]+siz[x]-1,k,1,n,1);//子树区间右端点为tid[x]+siz[x]-1
    178 }
    179 
    180 int getsum_subtree(int x)//子树求和
    181 {
    182     return query(tid[x],tid[x]+siz[x]-1,1,n,1);
    183 }
    184 
    185 int main()
    186 {
    187     scanf("%d%d%d%d",&n,&m,&r,&mod);
    188     init();
    189     for(int i=1;i<=n;i++)
    190         scanf("%d",&w[i]);//点权
    191     for(int i=1;i<=n-1;i++){
    192         int u,v;
    193         scanf("%d%d",&u,&v);
    194         add(u,v);
    195         add(v,u);
    196     }
    197     dfs1(r,0);//根节点
    198     dfs2(r,r);
    199     build(1,n,1);
    200     while(m--){
    201         int op,x,y,z;
    202         scanf("%d",&op);
    203         if(op==1){
    204             scanf("%d%d%d",&x,&y,&z);
    205             update_path(x,y,z);
    206         }
    207         else if(op==2){
    208             scanf("%d%d",&x,&y);
    209             printf("%d
    ",getsum_path(x,y));
    210         }
    211         else if(op==3){
    212             scanf("%d%d",&x,&z);
    213             update_subtree(x,z);
    214         }
    215         else if(op==4){
    216             scanf("%d",&x);
    217             printf("%d
    ",getsum_subtree(x));
    218         }
    219     }
    220     return 0;
    221 }

    溜了溜了。。。

  • 相关阅读:
    sax解析xml案例二
    mysql之删除重复数据
    Android之Intent探究
    struts2之Action名称的搜索顺序
    struts2自定义拦截器二——模拟session超时的处理
    struts2之防止表单重复提交
    Android之日期及时间选择对话框
    sax解析xml案例一
    Android之单选按钮对话框
    查询修改nls_database_parameters系统配置
  • 原文地址:https://www.cnblogs.com/ZERO-/p/9762991.html
Copyright © 2011-2022 走看看