AC日记——【模板】树链剖分 洛谷 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取模)
输入输出样例
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
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的更多相关文章
- [luogu P3384] [模板]树链剖分
[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...
- luoguP3384 [模板]树链剖分
luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...
- [洛谷P3384] [模板] 树链剖分
题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...
- 模板 树链剖分BFS版本
//点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...
- AC日记——红色的幻想乡 洛谷 P3801
红色的幻想乡 思路: 线段树+容斥原理: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #de ...
- AC日记——无线网络发射器选址 洛谷 P2038
题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形成的网格状,并且相邻 ...
- AC日记——小A的糖果 洛谷七月月赛
小A的糖果 思路: for循环贪心: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #defi ...
- AC日记——矩阵取数游戏 洛谷 P1005
矩阵取数游戏 思路: dp+高精: 代码: #include <bits/stdc++.h> using namespace std; #define ll long long struc ...
- AC日记——妖梦拼木棒 洛谷 P3799
妖梦拼木棒 思路: 神特么题: 代码: #include <bits/stdc++.h> using namespace std; #define mod 1000000007LL int ...
随机推荐
- Ralph W. Tyler【拉尔夫·泰勒】
Ralph W. Tyler Anyone who cares about what schools and colleges teach and how their student learn wi ...
- kafka 的offset的重置
最近在spark读取kafka消息时,每次读取都会从kafka最新的offset读取.但是如果数据丢失,如果在使用Kafka来分发消息,在数据处理的过程中可能会出现处理程序出异常或者是其它的错误,会造 ...
- SSH无密码登录及远程拷贝命令SCP的使用
SSH无密码登录 1.生成密钥对(公钥和私钥) $ cd /home/cen/.ssh $ ssh-keygen -t rsa #生成密钥,使用rsa方式进行加密,四个回车 $ ssh-copy-id ...
- Android 简历+面试题 汇总
1.教你写简历 1.1.你真的会写简历吗? 1.2.80%以上简历都是不合格的 1.3.推荐两个技术简历模板 1.4.关于程序员求职简历 1.5.程序员简历模板列表 2.面试题 2.1.国内一线互联网 ...
- input框中的必填项之取消当前input框为必填项
html5新增了一个required属性,可以使用这个属性对文本框设置必填项,直接在input文本框上添加required即可 . 效果如图:
- mysql初始化失败的问题
首先:my.ini 配置文件中 路径需要改成自己电脑mysql解压的路径. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ...
- StartWith 测试
var clientConfiguration = GetConfiguration("couchbase.json"); ClusterHelper.Initialize(cli ...
- leetcode 【 Pascal's Triangle 】python 实现
题目: Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,R ...
- 16进制转10进制 HDU-1720
A+B Coming Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- tinyipa make
参考:http://tinycorelinux.net/ Ironic Python Agent repo还提供了一组脚本,用于在imagebuild / tinyipa文件夹下构建一个基于Linux ...