题目描述

如题,已知一棵包含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<=10,M<=10

对于70%的数据:N<=1000,M<=1000

对于100%的数据:N<=100000,M<=100000

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

样例说明:

树的结构如下:

各个操作如下:

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

---------------------------------------x_x--------------------------------------

就是树链剖分

终于可以超熟练的在半小时内搞出树链剖分 好开心

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 150000
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
typedef long long LL;
int n,m,r,M;
struct Tree{
#define lson(x) (x<<1)
#define rson(x) ((x<<1)+1)
LL l[N*],r[N*],w[N*],sum[N*];
void upd(int x){sum[x]=(sum[lson(x)]+sum[rson(x)]+w[x]*(r[x]-l[x]+))%M;}
void build(int p,int L,int R)
{
l[p]=L;r[p]=R;w[p]=;
if(L==R)return;
int MID=(L+R)>>;
build(lson(p),L,MID);
build(rson(p),MID+,R);
}
void ins(int p,int x,int v)
{
int L=l[p],R=r[p];int MID=(L+R)>>;
if(L==R){
w[p]+=v;w[p]%=M;upd(p);return;
}
if(x<=MID)ins(lson(p),x,v);
else ins(rson(p),x,v);
upd(p);
}
void add(int x,int L,int R,int v)
{
int mid=(l[x]+r[x])>>;
if(l[x]==L&&r[x]==R)
{
w[x]+=v;w[x]%=M;upd(x);return;
}
if(R<=mid)add(lson(x),L,R,v);
if(L>mid)add(rson(x),L,R,v);
if(R>mid&&L<=mid)
{
add(lson(x),L,mid,v);
add(rson(x),mid+,R,v);
}
upd(x);
}
int getsum(int x,int L,int R)
{
int mid=(l[x]+r[x])>>;int tans=w[x]*(R-L+)%M;
if(l[x]==L&&r[x]==R)
{
return sum[x];
}
if(R<=mid)return (getsum(lson(x),L,R)+tans)%M;
if(L>mid)return (getsum(rson(x),L,R)+tans)%M;
if(R>mid&&L<=mid)
{
return (getsum(lson(x),L,mid)+getsum(rson(x),mid+,R)+tans)%M;
}
}
}T;
struct Graph{
struct E{
int next,to;
}edge[N*];
int head[N];int cnt;
bool vis[N];LL v[N],size[N],son[N],top[N],id[N],deep[N],fa[N],xid[N];
int sign;
void adde(int u,int v)
{
edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;
edge[++cnt].to=u;edge[cnt].next=head[v];head[v]=cnt;
} void dfs1(int u)
{
size[u]=;int S=;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(vis[v])continue;vis[v]=;
deep[v]=deep[u]+;fa[v]=u;
dfs1(v);
size[u]+=size[v];
if(!S||size[v]>size[S])S=v;
}
son[u]=S;
}
void dfs2(int u,int tp)
{
id[u]=++sign;
top[u]=tp;
if(!son[u]){xid[u]=sign;return;}
vis[son[u]]=;
dfs2(son[u],tp);
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;if(vis[v])continue;
vis[v]=;dfs2(v,v);
}
xid[u]=sign;
}
void prework()
{
cin>>n>>m>>r>>M;
For(i,,n)scanf("%d",&v[i]);
For(i,,n-)
{
int u,v;scanf("%d%d",&u,&v);
adde(u,v);
}
vis[r]=;
dfs1(r);
memset(vis,,sizeof(vis));
vis[r]=;
dfs2(r,r);
T.build(,,n);
For(i,,n)T.ins(,id[i],v[i]);
}
void add1(int u,int v,int w)
{
if(deep[top[u]]<deep[top[v]])swap(u,v);
while(top[u]!=top[v])
{
T.add(,id[top[u]],id[u],w);
u=fa[top[u]];
if(deep[top[u]]<deep[top[v]])swap(u,v);
}
if(deep[u]<deep[v])swap(u,v);
T.add(,id[v],id[u],w);
}
int get(int u,int v)
{
int ans=;
if(deep[top[u]]<deep[top[v]])swap(u,v);
while(top[u]!=top[v])
{
ans=(ans+T.getsum(,id[top[u]],id[u]))%M;
u=fa[top[u]];
if(deep[top[u]]<deep[top[v]])swap(u,v);
}
if(deep[u]<deep[v])swap(u,v);
ans=(ans+T.getsum(,id[v],id[u]))%M;
return ans%M;
}
void add2(int u,int w)
{
T.add(,id[u],xid[u],w);
}
int get2(int u)
{
return T.getsum(,id[u],xid[u])%M;
}
}G;
int main()
{
G.prework();
For(t,,m)
{
int opt;
scanf("%d",&opt);
int u,v,w;
if(opt==)
{
scanf("%d%d%d",&u,&v,&w);
G.add1(u,v,w);
}
if(opt==)
{
scanf("%d%d",&u,&v);printf("%d\n",G.get(u,v));
}
if(opt==)
{
scanf("%d%d",&u,&w);G.add2(u,w);
}
if(opt==)
{
scanf("%d",&u);printf("%d\n",G.get2(u));
}
}
}

P.S.有没有觉得我的代码超帅啊 结构体凶在那里 @sjj118

洛谷 P3384 树链剖分(模板题)的更多相关文章

  1. 洛谷P3384 树链剖分

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...

  2. 洛谷 P3384树链剖分 题解

    题面 挺好的一道树剖模板: 首先要学会最模板的树剖: 然后这道题要注意几个细节: 初始化时,seg[0]=1,seg[root]=1,top[root]=root,rev[1]=root; 在线段树上 ...

  3. 洛谷 [P3384] 树链剖分 模版

    支持各种数据结构上树,注意取膜. #include <iostream> #include <cstring> #include <algorithm> #incl ...

  4. BZOJ 2243 染色 | 树链剖分模板题进阶版

    BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...

  5. 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器

    刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...

  6. 洛谷 P2146 [NOI2015]软件包管理器 (树链剖分模板题)

    题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个 ...

  7. bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题

    [ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u ...

  8. 【树链剖分】洛谷P3379 树链剖分求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  9. BZOJ 1036 树的统计Count 树链剖分模板题

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将 ...

随机推荐

  1. 抽象工厂模式(Abstract Factory Pattern)

    动机(Motivation) 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象创建方法(new), ...

  2. C# WebBrowser控件使用教程与技巧收集

    常用的方法 Navigate(string urlString):浏览urlString表示的网址 Navigate(System.Uri url):浏览url表示的网址 Navigate(strin ...

  3. 【转】关于Oracle将小于1的数字to_char后丢掉0的解决办法

    SQL代码如下: select rtrim(to_char(0.11, 'fm9990.99'), '.') from dual; 其中0.11为需要to_char的数字fm去掉字符串前面的空格999 ...

  4. TreeMap 的实现

    TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 Tre ...

  5. mysql 获取当前日期及格式化 (转)

    MYSQL 获取当前日期及日期格式获取系统日期: NOW()格式化日期: DATE_FORMAT(date, format)注: date:时间字段format:日期格式 返回系统日期,输出 2009 ...

  6. Type.GetType()反射另外项目中的类时返回null的解决方法

    项目1:ProjectA namespace ProjectA { public class paa { .... } } Type.GetType("paa")返回null Ty ...

  7. matlab初学之句柄

    文章出处:http://www.cnblogs.com/CBDoctor/archive/2012/04/06/2434072.html 在matlab中,每一个对象都有一个数字来标识,叫做句柄.当每 ...

  8. string和char*的相互转换

    原文地址: 点击打开链接

  9. 浅谈javascript中事件冒泡与事件捕获

    事件冒泡:一个事件会顺着他的层级一直往上传,一直传到document上为止,即从盒模型上看是从内到外的过程. 例: <!DOCTYPE html><html lang="e ...

  10. Ngui中Sprite,SlicedSprite,Tiled Sprite,FilledSprite的区别

    Sprite:标准Sprite控件,自适应图片大小. Sliced Sprite:一个含有9个切片的Sprite,创建固定边框的控件最佳选择,固定大小,不会随图片大小而改变,可做人物头像等. Tile ...