「JOISC 2022 Day3」洒水器

该博客讨论了一种针对树结构的数据修改和查询问题,其中涉及点权的更新以及对距离限定范围内的节点进行操作。博主提出了一种使用标记法的解决方案,通过将修改操作转化为对祖先节点的标记更新,确保了修改的完整性和效率。算法核心在于利用ddd的较小限制,避免了重复计算,并给出了具体的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给你一棵树,每个点有一个点权hih_ihi,修改操作:把距离xxx小于等于ddd的点的点权乘上www,对LLL取模,LLL在修改和查询前给出,查询某一个点的点权

题解

一开始看错题了,没发现d≤40d\le 40d40,由于ddd很小,考虑有关ddd的做法,我们不妨把修改全部用一种标记来表示,设tagx,ytag_{x,y}tagx,y表示在xxx子树中距离yyy的点的点权需要乘上tagx,ytag_{x,y}tagx,y,只用这种标记能否不重不漏的覆盖所有修改的点呢,答案是可行的,比如说有一个修改x,d,wx,d,wx,d,w,可以把xxxddd级祖先都找出来,记为fafafa,对于它们都这样打上标记tagfa,d−dis(fa,x)∗=w,tagfa,d−dis(fa,x)−1∗=wtag_{fa,d-dis(fa,x)}*=w,tag_{fa,d-dis(fa,x)-1}*=wtagfa,ddis(fa,x)=w,tagfa,ddis(fa,x)1=w,不难发现这样就能覆盖所有需要修改的点,并且不会修改多次

code

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
#define gc() (fp==fq&&(fq=(fp=fr)+fread(fr,1,1<<20,stdin))==fp?EOF:*fp++)
char fr[1<<21],*fp,*fq;
void read(int &res)
{
	res=0;char ch=gc();
	while(ch<'0'||ch>'9') ch=gc();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=gc();
}
void read(ll &res)
{
	res=0;char ch=gc();
	while(ch<'0'||ch>'9') ch=gc();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=gc();
}
void write(ll res)
{
	if(res>=10) write(res/10),putchar('0'+res%10);
	else putchar('0'+res);
}
const int N=2e5+1000,B=40;
int n,q,fa[N+10],st[N+10],tot;ll l,h[N+10],tag[B+10][N+10];
struct edge
{
	int to,last;
}e[N<<1|1];
void add(int a,int b)
{
	e[++tot].to=b;
	e[tot].last=st[a];
	st[a]=tot;
}
void dfs(int u)
{
	for(int i=st[u],v;i!=0;i=e[i].last)
	{
		v=e[i].to;
		if(v==fa[u]) continue;
		fa[v]=u,dfs(v);
	}
}
void pushtag(int u,int d,ll k)
{
	if(d<0) return;
	if(d!=0) tag[d-1][u]*=k,tag[d-1][u]%=l;
	tag[d][u]*=k,tag[d][u]%=l;
	pushtag(fa[u],d-1,k);
}
ll querymul(int u,int d)
{
	if(d>40) return 1;
	return querymul(fa[u],d+1)*tag[d][u]%l;
}
int main()
{
	read(n),read(l);
	for(int i=1,a,b;i<n;i++) read(a),read(b),add(a,b),add(b,a);
	dfs(1);
	for(int i=1;i<=n;i++) read(h[i]);
	for(int i=0;i<=n;i++) for(int j=0;j<=B;j++) tag[j][i]=1;
	read(q);
	for(int t,x,d,w;q--;)
	{
		read(t),read(x);
		if(t==1) read(d),read(w),pushtag(x,d,w);
		else write(h[x]*querymul(x,0)%l),putchar('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值