【悬关】28pts AC #2#3#11 求调
查看原帖
【悬关】28pts AC #2#3#11 求调
714692
zzy_vector楼主2024/10/15 11:09

#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;
}
2024/10/15 11:09
加载中...