升!龙!
题目链接:https://ac.nowcoder.com/acm/contest/106318/G
题意:
给定一颗子树,规定其val为 从根节点1到叶子节点的简单路径的权值之和 的max
现在给出q次查询,每次查询将x节点连接到y节点之下,求此时的val(每次操作不改变原树)
思路:
DFS序(树形dp)+查询最大值
发现每次移动都只会改变x节点及其子树的路径权值和(从x节点的father变为y节点)
所以我们要在答案中刨去原子树 x节点及其子树的答案 ,并与更新后的 x节点及其子树的答案 取max即可
为了实现要求,需要在DFS进行树形DP
我们需要记录:
w[i]:从根节点1到i节点的路径权值
f[i]:从节点i到其子树的 路径权值和 的最大值
那么移动后 x节点及其子树的答案 即为w[y]+f[x]
我们也需要知道原先刨去 x节点及其子树的答案 的答案
通过DFS序将每个节点映射到一维数组
记录lev[i]:节点i 刚好搜索完子树 此时的节点
所以 节点i及其子树 在一维数组中代表的区间即为:[i,lev[i]]
那么[1,i-1],[lev[i]+1,n]即为刨去后的区间
显然答案就是两个区间w[i]的最大值
我们需要快速查询两个区间,可以用线段树维护,也可ST表,也可 前缀最大值+后缀最大值
vector<int>e[maxn];
int f[maxn];
int w[maxn];
int a[maxn];
int n;
map<int,int>mp;
int cnt;
int lev[maxn];
void dfs(int u,int fa,int now){
w[u]=now+a[u];
mp[u]=++cnt;
int res=a[u];
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u,w[u]);
res=max(res,f[v]+a[u]);
}
lev[u]=cnt;
f[u]=res;
}
int pre[maxn];
int suf[maxn];
int dfsn[maxn];
void solve(){
int q;cin>>n>>q;
rep(i,1,n)cin>>a[i];
for(int i=2;i<=n;i++){
int p;cin>>p;
e[p].pb(i);e[i].pb(p);
}
dfs(1,0,0);
for(auto x:mp){
dfsn[x.se]=x.fi;
}
rep(i,1,n)pre[i]=max(pre[i-1],w[dfsn[i]]);
for(int i=n;i>=1;i--)suf[i]=max(suf[i+1],w[dfsn[i]]);
rep(i,1,q){
int x,y;cin>>x>>y;
int l=mp[x],r=lev[x];
int ans=0;
ans=max(ans,f[x]+w[y]);
ans=max(ans,max(pre[l-1],suf[r+1]));
cout<<ans<<endl;
}
}

浙公网安备 33010602011771号