描述
在大小为 N 的树上,点从 1 到 N 标号,第 i 个点有权值 Ai,现在需要支持两 种操作:
第一种操作格式为“1 U” ,表示询问从 U 出发的简单路径,经过的点权值 之和的最大值;
第二种操作格式为“2 U V” ,表示将 U 的权值修改为 V。
输入
第一行两个整数 N 和 M,表示树的大小和操作数;
第二行 N-1 个整数,第 i 个整数 Pi(1<=Pi<=i)表示第 i+1 个点与 Pi 有边相 连;
第三行 N 个整数,第 i 个整数 Ai 表示第 i 个点的点权;
接下来 M 行,每行为一个询问操作“1 U” 或修改操作“2 U V” ,按操作发 生的先后顺序给出。
输出
对于每个询问操作,输出一个整数,即经过的点权值之和的最大值
样例输入
6 7
1 1 1 3 3
-1 2 -3 4 -5 6
1 2
1 5
1 6
2 4 5
1 2
1 5
1 6
样例输出
5
-2
6
6
-2
7
提示
对于 10%的数据,满足:
1<=N<=1000,1<=M<=1000
对于另外 20%的数据,不存在修改操作
对于另外 20%的数据,满足 Pi=i
对于 100%的数据,满足:
1<=N<=100000, 1<=M<=100000
1<=Pi<=i, -10000<=Ai<=10000
1<=U<=n, -10000<=V<=10000
时限4s,空间限制64MB
解析:
实际上按照SCOI的尿性,这道题时间限制没出成1s都已经算是很仁慈了。
但是64MB的空间限制卡点分树直接把一大波人送退役。
于是有神仙提出了什么这道题的DDP做法。
还有什么两个log的链分治。
实际上如果你做完过SPOJ-QTREE系列,这道题的LCT做法相当SB。(虽然也是两个log)
由于我们需要维护子树信息且全局没有Link和cut操作,所以我们放弃makeroot。
每个点开multiset维护向所有虚子树叉出去的最大路径权值和。
实链上维护链顶和链底在当前链上以及当前链上所有点子树中的答案。
显然就可以轻易在access和pushup的时候维护了。
对于询问,直接将询问的点access变成链底,然后回答当前链底的答案就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
#define int ll
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
re bool f=0;
while(!isdigit(c=gc()))f|=c=='-';re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
using std::cout;
using std::cerr;
cs int N=1e5+1000;
int n,m;
int fa[N],son[N][2];
int sum[N],val[N],lmx[N],rmx[N];
std::multiset<int> s[N];
inline int fir(cs std::multiset<int> &s){return (!s.size())?-0x3f3f3f3f:*--s.end();}
inline void pushup(int u){
sum[u]=val[u]+sum[son[u][0]]+sum[son[u][1]];
lmx[u]=std::max(lmx[son[u][0]],sum[son[u][0]]+val[u]+std::max(std::max(0ll,lmx[son[u][1]]),fir(s[u])));
rmx[u]=std::max(rmx[son[u][1]],sum[son[u][1]]+val[u]+std::max(std::max(0ll,rmx[son[u][0]]),fir(s[u])));
}
inline bool isroot(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
inline bool which(int u){return son[fa[u]][1]==u;}
inline void Rotate(int u){
int Fa=fa[u],FA=fa[Fa];
bool pos=which(u);
if(!isroot(Fa))son[FA][which(Fa)]=u;
son[Fa][pos]=son[u][!pos];
if(son[u][!pos])fa[son[Fa][pos]]=Fa;
son[u][!pos]=Fa;
fa[Fa]=u,fa[u]=FA;
pushup(Fa),pushup(u);
}
inline void Splay(int u){
for(int re Fa=fa[u];!isroot(u);Rotate(u),Fa=fa[u])
if(!isroot(Fa))Rotate(which(Fa)==which(u)?Fa:u);
}
inline void access(int u){
for(int re ch=0;u;u=fa[ch=u]){
Splay(u);
if(son[u][1])s[u].insert(lmx[son[u][1]]);
if(ch){
std::multiset<int>::iterator iter=s[u].find(lmx[ch]);
if(iter!=s[u].end())s[u].erase(iter);
}
son[u][1]=ch;
pushup(u);
}
}
signed main(){
lmx[0]=rmx[0]=-0x3f3f3f3f;
n=getint(),m=getint();
for(int re i=2;i<=n;++i)fa[i]=getint();
for(int re i=1;i<=n;++i)val[i]=getint();
for(int re i=n;i;--i)pushup(i),s[fa[i]].insert(lmx[i]);
while(m--)switch(getint()){
case 1:{
int u=getint();
access(u);
Splay(u);
cout<<rmx[u]<<"\n";
break;
}
case 2:{
int u=getint();
access(u);
Splay(u);
val[u]=getint();
pushup(u);
break;
}
}
return 0;
}