#1 的样例:
input:
8 10 2 448348
458 718 447 857 633 264 238 944
1 2
2 3
3 4
6 2
1 5
5 7
8 6
3 7 611
4 6
3 1 267
3 2 111
1 6 3 153
3 7 673
4 8
2 6 1
4 7
3 4 228
正确output:
1208
1055
2346
1900
我的output:
1208
1055
2346
1633
代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
#include<map>
#include<set>
using namespace std;
int n,m,r,mod,pw[500005],x,y,z,opt;//pw为点权
struct node{
int son;
int val;
int nex;
}edge[500005*2];
int head[500005],cnt;
int dep[500005],siz[500005],pa[500005],son[500005];//节点深度,节点的子树大小,节点的父亲,节点的重儿子编号
int tot,id[500005],pw2[500005],top[500005];//节点新编号,节点在链中的新编号,新的节点序列中的节点的值,每一个链的顶(深度最小的点)
struct tree{
int pre;
int l,r;
int tag;
}t[500005*4];
int read(){
int number=0,r=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
r=-1;
}
ch=getchar();
}
while(ch<='9'&&ch>='0'){
number=number*10+(ch-'0');
ch=getchar();
}
return number*r;
}
void add(int u,int v){
cnt++;
edge[cnt].son=v;
edge[cnt].nex=head[u];
head[u]=cnt;
}
//-----------------树链剖分部分-------------------------------
void dfs1(int x,int fa,int d){//x当前节点,fa是父亲,d是深度
dep[x]=d;
pa[x]=fa;
siz[x]=1;//初始化节点的子树大小为1
int maxson=-1;//记录重儿子的儿子数
for (int i=head[x];i!=0;i=edge[i].nex){
int r=edge[i].son;
if(r==pa[x]){
//如果是该节点的父亲,那么显然不能更新
//即r==fa
continue;
}
dfs1(r,x,d+1);//向下搜索
siz[x]+=siz[r];//x的子树大小是其所有儿子的子树大小之和
if(siz[r]>maxson){//如果x的儿子r的子树大小比当前的重儿子要大
maxson=siz[r];//更新重儿子的儿子的个数
son[x]=r;//记录x的重儿子变为r
}
}
}
void dfs2(int x,int tp){//x为当前节点,tp为该练的顶端
id[x]=++tot;//记录节点x的新的序列编号为tot
pw2[tot]=pw[x];//记录新的节点的点权为pw[x]
top[x]=tp;//x所在的链的顶端是tp
if(!son[x]){
return;//没有儿子了,直接回溯
}
dfs2(son[x],tp);//先处理重儿子
for (int i=head[x];i!=0;i=edge[i].nex){
int r=edge[i].son;
if(r==pa[x]||r==son[x]){
continue;//父亲和重儿子都直接跳过,因为我们找的是轻边
}
dfs2(r,r);//一个轻边,重新开始
}
}
//-----------------------线段树部分----------------------
void build(int p,int l,int r){
t[p].l=l;
t[p].r=r;
if(l==r){
t[p].pre=pw2[l];
if(t[p].pre>mod){
t[p].pre=t[p].pre%mod;//防止溢出
}
return;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].pre=(t[p*2].pre+t[p*2+1].pre)%mod;
}
void lazy(int p){
if(t[p].tag){
t[p*2].tag+=t[p].tag;
t[p*2+1].tag=t[p].tag;
t[p*2].pre+=t[p].tag*(t[p*2].r-t[p*2].l+1);
t[p*2+1].pre+=t[p].tag*(t[p*2+1].r-t[p*2+1].l+1);
t[p*2].pre%=mod;
t[p*2+1].pre%=mod;
t[p].tag=0;
}
}
void change_tree(int p,int x,int y,int k){
if(x<=t[p].l&&y>=t[p].r){
t[p].tag+=k;
t[p].pre+=k*(t[p].r-t[p].l+1);
//t[p].pre%=mod;
return;
}
lazy(p);
int mid=(t[p].l+t[p].r)>>1;
if(x<=mid){
change_tree(p*2,x,y,k);
}
if(y>mid){
change_tree(p*2+1,x,y,k);
}
t[p].pre=(t[p*2].pre+t[p*2+1].pre)%mod;
}
int query(int p,int x,int y){
int res=0;
if(x<=t[p].l&&y>=t[p].r){
return t[p].pre;
}
lazy(p);
int mid=(t[p].l+t[p].r)>>1;
if(x<=mid){
res=(res+query(p*2,x,y))%mod;
}
if(y>mid){
res=(res+query(p*2+1,x,y))%mod;
}
return res;
}
//----------------------操作部分-------------------
void change1(int x,int y,int k){//树剖求LCA
k%=mod;
while(top[x]!=top[y]){//两个点不在同一个链上
if(dep[top[x]]<dep[top[y]]){
swap(x,y);//让x变成顶点的深度较大的那一个
}
change_tree(1,id[top[x]],id[x],k); //修改从x到x的顶点的区间和
x=pa[top[x]];//跳到链的顶端
}
if(dep[x]>dep[y]){
swap(x,y);
}
change_tree(1,id[x],id[y],k);
}
int sigma1(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
ans+=query(1,id[top[x]],id[x]);
ans%=mod;
x=pa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
}
ans+=query(1,id[x],id[y]);
ans%=mod;
return ans;
}
void change2(int p,int k){
change_tree(1,id[x],id[x]+siz[x]-1,k);
//显然在新的序列中。id[x]和id[x]+siz[x]中为一个子树
}
int sigma2(int p){
int ans=0;
ans=query(1,id[x],id[x]+siz[x]-1);
//ans%=mod;
return ans;
}
int main(){
//freopen("P3398_1.in","r",stdin);
//freopen("P3398_1.out","w",stdout);
n=read();
m=read();
r=read();
mod=read();
for (int i=1;i<=n;i++){
pw[i]=read();
}
for (int i=1;i<n;i++){
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs1(r,0,1);//找轻重链
dfs2(r,r);//开始划分重链
build(1,1,n);//建立线段树
for (int i=1;i<=m;i++){
opt=read();
if(opt==1){
//将树上从x到y的最短路径上的节点的值均加上z
//最短路径显然要经过LCA
x=read();
y=read();
z=read();
change1(x,y,z);
}
else if(opt==2){
//求树从x到y结点最短路径上所有节点的值之和
x=read();
y=read();
printf("%d\n",sigma1(x,y));
}
else if(opt==3){
//以x为根节点的子树内所有节点值都加上z
x=read();
z=read();
change2(x,z);
}
else if(opt==4){
//求以x为根节点的子树内所有节点值之和
x=read();
printf("%d\n",sigma2(x));
}
}
return 0;
}