【学习笔记】「JOISC 2022 Day3」洒水器
我是小丑。
有没有一种恰好将所有 ≤ d \le d ≤d的点都标记一次的打标方法?
答案是有的。设 f i , d f_{i,d} fi,d表示以 i i i为根的子树,第 d d d层的儿子打上的标记。然后发现,从 x x x出发往上爬到祖先 y y y,假设到 y y y点时上还有 d d d步可以走,那么就把以 y y y为根的子树的第 d d d层和第 d − 1 d-1 d−1层打上标记。
这样每个点恰好标记一次。厉害。不需要用到任何数据结构。
复杂度 O ( n d ) O(nd) O(nd)。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+55;
int n,L,Q;
ll f[N][45],h[N],fa[N];
vector<int>G[N];
//joker
void dfs(int u,int topf){
fa[u]=topf;
for(auto v:G[u]){
if(v!=topf){
dfs(v,u);
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>L;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
G[u].pb(v),G[v].pb(u);
}
for(int i=n+1;i<=n+40;i++){
G[i].pb(i-1),G[i-1].pb(i);
}
for(int i=1;i<=n;i++)cin>>h[i];
for(int i=1;i<=n+40;i++){
for(int j=0;j<=40;j++)f[i][j]=1;
}
dfs(n+40,0);
cin>>Q;
for(int i=1;i<=Q;i++){
int op;
cin>>op;
if(op==1){
int x,D,W;
cin>>x>>D>>W;
while(x&&D>=0){
f[x][D]=f[x][D]*W%L;
if(D)f[x][D-1]=f[x][D-1]*W%L;
x=fa[x],D--;
}
}
else{
int x,D=0;
cin>>x;
ll res=h[x];
while(x&&D<=40){
res=res*f[x][D]%L;
x=fa[x],D++;
}
cout<<res<<"\n";
}
}
}