Description
Solution
算法一
对于每次询问跑一遍 dp \text{dp} dp 计算答案即可。期望得分 5 5 5 分。
void dfs(int now,int fath){
for (int i=head[now];i;i=e[i].nxt){
int y=e[i].to;
if (y==fath) dfs(y,now);
dp[now]+=max(dp[y],0);
}
dp[now]+=a[now];
}
算法二
为方便叙述,令所有节点的点权增量为 △ \triangle △, w u w_u wu 为节点 u u u 的权值, m m m 为所有输入的数的最大值。
观察算法一的 dp \text{dp} dp 式,不难发现: 若节点 v v v 会对其祖先节点 f u f_u fu 产生 w v w_v wv 的贡献,当且仅当从 u u u 为 v v v 的祖先且 u u u 到 v v v 的路径上各个点的 f f f 值均不小于 0 0 0。
注意到,节点权值虽然会因为 △ \triangle △ 的变化而变化,但 f f f 值始终是随着 △ \triangle △ 的变大而变大的,所以我们可以定义状态 g u g_u gu,表示满足 f u ≥ 0 f_u \ge 0 fu≥0 的最小 △ \triangle △。
先思考如何求出 g u g_u gu。
不难想到二分,令当前二分的值为 δ \delta δ。根据上述性质, δ \delta δ 合法当且仅当
0 ≤ w u + ∑ v ∈ subtree ( u ) , u ≠ v [ max p ∈ path ( u , v ] { g p } ≤ δ ] × ( w v + δ ) 0 \le w_u+\sum_{v \in \text{subtree}(u),u \neq v} \left[\max_{p \in \text{path}(u,v]} \{g_p\} \le \delta\right] \times (w_v + \delta) 0≤wu+v∈subtree(u),u=v∑[p∈path(u,v]max{gp}≤δ]×(wv+δ)
该如何快速求出上面的值呢?考虑线段树合并+线段树二分。具体来说,线段树上下标为 x x x 的位置记录了满足 max p ∈ path ( u , v ] { g p } = x \max_{p \in \text{path}(u,v]} \{g_p\}=x maxp∈path(u,v]{gp}=x 的节点 v v v 所对应的 w v w_v wv 之和以及这样的 v v v 的数量。那么为求出 g u g_u gu,我们可以先将各个儿子的权值线段树合并在一起,然后在线段树上二分并在递归的同时维护一段前缀的信息。最后我们更新线段树,即将所有 [ − m , g u ) [-m,g_u) [−m,gu) 累加到 g u g_u gu 处并清空 [ − m , g u ) [-m,g_u) [−m,gu)。
从而,我们 O ( n log m ) O(n \log m) O(nlogm) 地求出了 g g g。那么该如何计算答案呢?
其实,计算答案与预处理 g g g 的唯一区别在于固定了 δ \delta δ。显然,直接做前缀查询就可以搞定了。不过,在合并过程中 u u u 处的权值线段树已经消匿了,因此我们需要对询问离线。
这样一来,总时空复杂度为 O ( n log m ) O(n \log m) O(nlogm)。期望得分 [ 77 , 100 ] [77,100] [77,100] 分。
Code
被卡常了,喷出题人。因此下面是 77 77 77 分的代码。。。
#include <bits/stdc++.h>
#define ll long long
#define PA pair<int,long long>
#define fi first
#define se second
#define MP make_pair
using namespace std;
const int maxl=1000005,maxg=80;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
return s*w;
}
int n,m,q,tot,cnt;
int head[maxl],w[maxl],t[maxl],root[maxl],deg[maxl];ll ans[maxl];
vector<PA > ve[maxl];
struct edge{int nxt,to;}e[maxl];
struct Segment_tree{int lson,rson,tag,cnt;ll sum;}tree[maxl*maxg];
namespace ST{
bool is_leaf(int x){return (!(tree[x].lson|tree[x].rson));}
void pushup(int rt){
int ls=tree[rt].lson,rs=tree[rt].rson;
tree[rt].cnt=tree[ls].cnt+tree[rs].cnt;
tree[rt].sum=tree[ls].sum+tree[rs].sum;
}
int Clone(int rt){
if (!rt) rt=++tot;
return rt;
}
void F(int rt){tree[rt].cnt=tree[rt].sum=0,tree[rt].tag=1;}
void pushdown(int rt){
tree[rt].lson=Clone(tree[rt].lson);
tree[rt].rson=Clone(tree[rt].rson);
if (tree[rt].tag){
int ls=tree[rt].lson,rs=tree[rt].rson;
F(ls),F(rs),tree[rt].tag=0;
}
}
void Pushdown(int rt){
if (tree[rt].tag){
F(tree[rt].lson),F(tree[rt].rson);
tree[rt].tag=0;
}
}
int change(int nl,int l,int r,int rt,PA del){
if (!rt) rt=++tot;
if (l==r){
tree[rt].cnt+=del.fi;
tree[rt].sum+=del.se;
return rt;
}
pushdown(rt);
int mid=(l+r)>>1;
if (nl<=mid) tree[rt].lson=change(nl,l,mid,tree[rt].lson,del);
else tree[rt].rson=change(nl,mid+1,r,tree[rt].rson,del);
pushup(rt);
return rt;
}
int assign(int nl,int nr,int l,int r,int rt){
if (!rt) rt=++tot;
if (nl<=l&&r<=nr){
tree[rt].cnt=tree[rt].sum=0,tree[rt].tag=1;
return rt;
}
pushdown(rt);
int mid=(l+r)>>1;
if (nl<=mid) tree[rt].lson=assign(nl,nr,l,mid,tree[rt].lson);
if (nr>mid) tree[rt].rson=assign(nl,nr,mid+1,r,tree[rt].rson);
pushup(rt);
return rt;
}
PA query(int nl,int nr,int l,int r,int rt){
if (nl<=l&&r<=nr) return MP(tree[rt].cnt,tree[rt].sum);
Pushdown(rt);
int mid=(l+r)>>1;PA res;
res.fi=res.se=0;
if (tree[rt].lson&&nl<=mid) res=query(nl,nr,l,mid,tree[rt].lson);
if (tree[rt].rson&&nr>mid){
PA tmp=query(nl,nr,mid+1,r,tree[rt].rson);
res.fi+=tmp.fi,res.se+=tmp.se;
}
return res;
}
int Merge(int x,int y,int l,int r){
if (is_leaf(x)) swap(x,y);
if (is_leaf(y)){
tree[x].cnt+=tree[y].cnt;
tree[x].sum+=tree[y].sum;
return x;
}
pushdown(x),pushdown(y);
int mid=(l+r)>>1;
tree[x].lson=Merge(tree[x].lson,tree[y].lson,l,mid);
tree[x].rson=Merge(tree[x].rson,tree[y].rson,mid+1,r);
pushup(x);
return x;
}
int work(int pos){
int now=root[pos],l=-m,r=m,res=0;
ll precnt=0,presum=0;
while (l<r){
int mid=(l+r)>>1,ls=tree[now].lson,rs=tree[now].rson;
pushdown(now);
ll cntl=tree[ls].cnt+precnt,suml=tree[ls].sum+presum;
if ((cntl+1)*mid+suml+w[pos]>=0) res=mid,now=ls,r=mid;
else now=rs,l=mid+1,precnt+=tree[ls].cnt,presum+=tree[ls].sum;
}
return res;
}
}
namespace ducati{
void add_edge(int u,int v){
cnt++;
e[cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
}
void get_all_in(){
n=read(),q=read();
for (int i=2;i<=n;i++){
int fa=read();
add_edge(fa,i),deg[fa]++;
}
for (int i=1;i<=n;i++) w[i]=read(),m=max(m,abs(w[i]));
for (int i=1;i<=q;i++){
int u=read(),x=read();
m=max(m,abs(x)),ve[u].push_back(MP(i,x));
}
m++;
}
void dfs(int now,int fath){
if (!deg[now]){
t[now]=-w[now];
root[now]=ST::change(t[now],-m,m,root[now],MP(1,w[now]));
for (int i=0;i<ve[now].size();i++){
int id=ve[now][i].fi,delta=ve[now][i].se;
ans[id]=w[now]+delta;
}
return;
}
int cnt_son=0;
for (int i=head[now];i;i=e[i].nxt){
int y=e[i].to;
if (y==fath) continue;
dfs(y,now);
if (cnt_son) root[now]=ST::Merge(root[now],root[y],-m,m);
else root[now]=root[y];
cnt_son++;
}
t[now]=ST::work(now);
for (int i=0;i<ve[now].size();i++){
int id=ve[now][i].fi,delta=ve[now][i].se;
PA sumv=ST::query(-m,delta,-m,m,root[now]);
ll res=(ll)sumv.fi*delta+sumv.se+w[now]+delta;
ans[id]=res;
}
PA tmp;
tmp=ST::query(-m,t[now]-1,-m,m,root[now]);
root[now]=ST::assign(-m,t[now]-1,-m,m,root[now]);
tmp.fi++,tmp.se+=w[now];
root[now]=ST::change(t[now],-m,m,root[now],tmp);
}
void print(){
for (int i=1;i<=q;i++) printf("%lld\n",ans[i]);
}
void solve(){get_all_in(),dfs(1,0),print();}
}
signed main(){
ducati::solve();
return 0;
}