题目描述

如题,已知一棵包含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(重要的事情说三遍:记得取模)

思路:

  裸树剖;

来,上代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> #define maxn 100001
#define LL long long int using namespace std; struct EdgeType {
LL to,next;
};
struct EdgeType edge[maxn<<]; struct TreeNodeType {
LL l,r,dis,mid,flag;
};
struct TreeNodeType tree[maxn<<]; LL if_z,tree_num,tree_dis[maxn],deep[maxn],cnt;
LL f[maxn],n,m,s,p,dis[maxn],flag[maxn],Enum;
LL size[maxn],end[maxn],belong[maxn],head[maxn]; char Cget; inline void read_int(LL &now)
{
now=,if_z=,Cget=getchar();
while(Cget>''||Cget<'')
{
if(Cget=='-') if_z=-;
Cget=getchar();
}
while(Cget>=''&&Cget<='')
{
now=now*+Cget-'';
Cget=getchar();
}
now*=if_z;
} inline void edge_add(LL from,LL to)
{
edge[++Enum].to=from,edge[Enum].next=head[to],head[to]=Enum;
edge[++Enum].to=to,edge[Enum].next=head[from],head[from]=Enum;
} inline void tree_up(LL now)
{
tree[now].dis=tree[now<<].dis+tree[now<<|].dis;
} void tree_build(LL now,LL l,LL r)
{
tree[now].l=l,tree[now].r=r;
if(l==r)
{
tree[now].dis=tree_dis[++tree_num];
return ;
}
tree[now].mid=(tree[now].l+tree[now].r)>>;
tree_build(now<<,l,tree[now].mid);
tree_build(now<<|,tree[now].mid+,r);
tree_up(now);
} inline void tree_down(LL now)
{
if(tree[now].l==tree[now].r) return ;
tree[now<<].dis+=(tree[now<<].r-tree[now<<].l+)*tree[now].flag;
tree[now<<].flag+=tree[now].flag;
tree[now<<|].dis+=(tree[now<<|].r-tree[now<<|].l+)*tree[now].flag;
tree[now<<|].flag+=tree[now].flag;
tree[now].flag=;
} void tree_change(LL now,LL l,LL r,LL x)
{
if(tree[now].l==l&&tree[now].r==r)
{
tree[now].dis+=(r-l+)*x;
tree[now].flag+=x;
return ;
}
if(tree[now].flag) tree_down(now);
if(l>tree[now].mid) tree_change(now<<|,l,r,x);
else if(r<=tree[now].mid) tree_change(now<<,l,r,x);
else
{
tree_change(now<<,l,tree[now].mid,x);
tree_change(now<<|,tree[now].mid+,r,x);
}
tree_up(now);
} LL tree_query(LL now,LL l,LL r)
{
if(tree[now].l==l&&tree[now].r==r)
{
return tree[now].dis;
}
if(tree[now].flag) tree_down(now);
tree_up(now);
if(l>tree[now].mid) return tree_query(now<<|,l,r);
else if(r<=tree[now].mid) return tree_query(now<<,l,r);
else return tree_query(now<<,l,tree[now].mid)+tree_query(now<<|,tree[now].mid+,r);
} void search(LL now,LL fa)
{
LL pos=cnt++;
deep[now]=deep[fa]+,f[now]=fa;
for(LL i=head[now];i;i=edge[i].next)
{
if(edge[i].to==fa) continue;
search(edge[i].to,now);
}
size[now]=cnt-pos;
} void search_(LL now,LL chain)
{
belong[now]=chain,flag[now]=++cnt;
tree_dis[flag[now]]=dis[now];
LL pos=;
for(LL i=head[now];i;i=edge[i].next)
{
if(flag[edge[i].to]!=) continue;
if(size[edge[i].to]>size[pos]) pos=edge[i].to;
}
if(pos!=) search_(pos,chain);
for(LL i=head[now];i;i=edge[i].next)
{
if(flag[edge[i].to]!=) continue;
search_(edge[i].to,edge[i].to);
}
end[now]=cnt;
} inline void solve_change(LL x,LL y,LL z)
{
while(belong[x]!=belong[y])
{
if(deep[belong[x]]<deep[belong[y]]) swap(x,y);
tree_change(,flag[belong[x]],flag[x],z);
x=f[belong[x]];
}
if(deep[x]<deep[y]) swap(x,y);
tree_change(,flag[y],flag[x],z);
} inline LL solve_query(LL x,LL y)
{
LL ans=;
while(belong[x]!=belong[y])
{
if(deep[belong[x]]<deep[belong[y]]) swap(x,y);
ans=(ans+tree_query(,flag[belong[x]],flag[x]))%p;
x=f[belong[x]];
}
if(deep[x]<deep[y]) swap(x,y);
ans=(ans+tree_query(,flag[y],flag[x]))%p;
return ans;
} int main()
{
read_int(n),read_int(m),read_int(s),read_int(p);
for(LL i=;i<=n;i++) read_int(dis[i]);
LL type,from,to,cur;
for(LL i=;i<n;i++)
{
read_int(from),read_int(to);
edge_add(from,to);
}
search(s,),cnt=,search_(s,s);
cnt=,tree_build(,,n);
for(LL i=;i<=m;i++)
{
read_int(type);
if(type==)
{
read_int(from),read_int(to),read_int(cur);
solve_change(from,to,cur);
}
if(type==)
{
read_int(from),read_int(to);
printf("%d\n",solve_query(from,to)%p);
}
if(type==)
{
read_int(from),read_int(to);
tree_change(,flag[from],end[from],to);
}
if(type==)
{
read_int(from);
printf("%d\n",tree_query(,flag[from],end[from])%p);
}
}
return ;
}

AC日记——【模板】树链剖分 洛谷 P3384的更多相关文章

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

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  2. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

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

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  4. 模板 树链剖分BFS版本

    //点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...

  5. AC日记——红色的幻想乡 洛谷 P3801

    红色的幻想乡 思路: 线段树+容斥原理: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #de ...

  6. AC日记——无线网络发射器选址 洛谷 P2038

    题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形成的网格状,并且相邻 ...

  7. AC日记——小A的糖果 洛谷七月月赛

    小A的糖果 思路: for循环贪心: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #defi ...

  8. AC日记——矩阵取数游戏 洛谷 P1005

    矩阵取数游戏 思路: dp+高精: 代码: #include <bits/stdc++.h> using namespace std; #define ll long long struc ...

  9. AC日记——妖梦拼木棒 洛谷 P3799

    妖梦拼木棒 思路: 神特么题: 代码: #include <bits/stdc++.h> using namespace std; #define mod 1000000007LL int ...

随机推荐

  1. Centos7(Linux)下安装VMware12

    https://blog.csdn.net/u012605477/article/details/65627234

  2. hihocoder#1098 : 最小生成树二·Kruscal算法

    #1098 : 最小生成树二·Kruscal算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 随着小Hi拥有城市数目的增加,在之间所使用的Prim算法已经无法继续使用 ...

  3. 记忆化搜索:POJ1088-滑雪(经典的记忆化搜索)

    skiing 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描述 Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑 ...

  4. Kali 远程登陆SSH

    一.配置SSH 编辑/etc/ssh/sshd_config 将#PasswordAuthentication no的注释去掉,将NO修改为YES //可以用密码登陆 将PermitRootLogin ...

  5. java集群技术

    序言 越来越多的关键应用运行在J2EE(Java 2, Enterprise Edition)中,这些诸如银行系统和账单处理系统需要高的可用性(High Availability, HA),同时像Go ...

  6. Session超时和莫名丢失的原因与处理办法

    原因: 改动global.asax,Web.config,bin目录里的东西,导致Web Applicatioin重启. 有些杀毒软件会去扫描你的Web.config文件,也会导致Session丢失. ...

  7. C 语言 习题 1-9

    练习1-9 编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替. #include <stdio.h> int main(int argc, char const *ar ...

  8. error C2011: “Picture”:“struct”类型重定义

    今天引用外来库时出现问题,也许是版本问题. 错误如下: .....\oursun\cincludes\quickdraw.h(309): error C2011: “Picture”:“struct” ...

  9. python-day3-之函数

    不使用函数的情况下只能遵循面向过程的编程,即,根据业务逻辑从上到下实现功能,往往重复的功能,使得代码出现重复. #最简单的函数调用 #由于python遵循从上到下读入内存的规律,所以函数要放到调用的前 ...

  10. Leetcode 629.K个逆序对数组

    K个逆序对数组 给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数. 逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且  ...