P9527 [JOISC2022] 洒水器 题解
题目传送门
以下设
暴力不行,于是考虑打标记。
注意到
显然这样会有点权被重复乘
下图给出了一个例子帮助理解:
图中
但模数可能没有逆元,这样做还是不行。
不妨换个角度,观察到
考虑对标记的定义稍作修改,设
这样,时间复杂度依然是
代码:
#include <iostream>
#include <vector>
const int N=214514,D=100;
int n,m,mod;
int a[N];
int tag[N][D],fa[N];
std::vector<int> e[N];
void dfs(int u,int f=0)
{
fa[u]=f;
for(int v:e[u])if(v!=f)dfs(v,u);
}
int main()
{
scanf("%d %d",&n,&mod);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n;i++)for(int j=0;j<=40;j++)tag[i][j]=1;
scanf("%d",&m);
int o,x,d,w;
while(m--)
{
scanf("%d %d",&o,&x);
if(o==1)
{
scanf("%d %d",&d,&w);
while(d>=0&&x)
{
tag[x][d]=1ll*tag[x][d]*w%mod;
if(d>=1)tag[x][d-1]=1ll*tag[x][d-1]*w%mod;
if(x==1) // 根节点要特判,因为根节点没有祖先抵消
{
for(int i=0;i<=d-2;i++)tag[1][i]=1ll*tag[1][i]*w%mod;
break;
}
x=fa[x];
d--;
}
}
else
{
int ans=a[x];
for(d=0;d<=40;d++) // 跳父亲累乘 tag
{
if(!x)break;
ans=1ll*ans*tag[x][d]%mod;
x=fa[x];
}
printf("%d\n",ans);
}
}
return 0;
}