题目传送门
题意大致理解:我们要维护一棵树,支持树上的单点修改,和子树的区间查询。
题意推敲:
单点查询不在话下,我们需要区间查询用数据结构去维护的话没有一个好的、简单的树上结构来维护他。
那么我们可以根据这个子树这个性质来把这个树上转化为线性的数据结构问题。不难想到 DFS 序,一个节点 &u& 的子树内的节点的 DFS 序比 \(u\) 大,并且全部在 \([\ \operatorname{dfn[u],dfn[u]+siz[u]}\ )\) 的范围内。这样可以漂亮地用线性数据结构或者线段树、树状数组啥的来搞这个区间最大和。
实现细节:
我们处理完建树以后 DFS 一遍来得出 DFN 和 \(\operatorname {siz[u]}\),我们考虑把 \(\operatorname{v[i]}\) 映射到 \(\operatorname{arr[dfn[i]]}\)。
然后我选择分块来写,无他,唯手熟尔。
CODE
#include<bits/stdc++.h>
using namespace std;
#define itn int
#define int long long
#define putcahr putchar
inline void read(int &x){x=0;int p=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')p=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=p;}
void write(int x){if(x<0){putchar('-');x*=-1;}if(x<10){putcahr(x+48);return;}write(x/10);putchar(x%10+48);}const int N=5e3+10,M=100;
int n,m,b[N],a[N],dfn[N],pos[N],L[M],R[M],siz[N],sum[M],len,cnt,tot;
vector<int>e[N];void init(){len=(int)sqrt(n);cnt=(n-1)/len+1;for(int i=1;i<=n;i++){pos[i]=(i-1)/len+1;}for(int i=1;i<=cnt;i++){L[i]=(i-1)*len+1;R[i]=min(n,i*len);sum[i]=*max_element(a+L[i],a+R[i]+1);}
}void dfs(itn x,int fa){siz[x]=1;dfn[x]=++tot;for(auto v:e[x]){if(v==fa)continue;dfs(v,x);siz[x]+=siz[v];}
}void update(int x,int c){a[x]=c;int res=0;for(int i=L[pos[x]];i<=R[pos[x]];i++){res=max(res,a[i]);}sum[pos[x]]=res;
}int catter(int l,int r){int res=0;for(int i=l;i<=r;i++){res=max(res,a[i]);}return res;
}int pre_block(int id){return sum[id];
}int query(int l,int r){if(pos[l]==pos[r]){return catter(l,r);}int res=0;res=max(res,catter(l,R[pos[l]]));res=max(res,catter(L[pos[r]],r));for(int i=pos[l]+1;i<pos[r];i++){res=max(res,pre_block(i));}return res;
}main(void){read(n);read(m);for(int i=1;i<=n;i++){read(b[i]);}for(int i=1,u,v;i<n;i++){read(u);read(v);e[u].push_back(v);e[v].push_back(u);}dfs(1,1);for(int i=1;i<=n;i++){a[dfn[i]]=b[i];}init();for(int i=1,opt,u,w;i<=m;i++){read(opt);if(opt==1){read(u),read(w);update(dfn[u],w);}else{read(u);write(query(dfn[u],dfn[u]+siz[u]-1));puts("");}}
}