解析
不难得到最优策略:先尽可能的快的送死直到路径畅通无组,然后一口气冲到t点。
现在的难点就在于如何尽可能的快的送掉特定的次数。
不难发现,花费时间关于死亡次数的函数必然是一个下凸包。
设 fx,if_{x,i}fx,i 表示子树内距离 xxx 不超过 i 的节点的最大 www。
设当前询问 (s,t)(s,t)(s,t) 路径上需要的最大死亡次数为 mxmxmx,找到最小的 i 满足 fs,i≥wf_{s,i}\ge wfs,i≥w,那么答案可以写成 mx∗i−∑j=1i−1fs,jmx*i-\sum_{j=1}^{i-1}f_{s,j}mx∗i−∑j=1i−1fs,j。
不难想到利用线段树来维护这个 fff。
但是暴力合并显然复杂度无法接受,由于这个距离与深度有关,考虑长链剖分,一条长链的点共用一个线段树。每次合并的时候把儿子的整条链一个点一个点暴力修改上去即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return f*x;
}const int N=3e5+100;
int n,m;
struct edge{int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){p[++cnt]=(edge){y,fi[x]};fi[x]=cnt;
}
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
struct Segment_Tree{int n;vector<ll>mn,laz,sum;void init(int L){mn.resize(L<<2);laz.resize(L<<2);sum.resize(L<<2);n=L-1;}inline void pushup(int k){mn[k]=min(mn[ls],mn[rs]);sum[k]=sum[ls]+sum[rs];return;}inline void add(int k,int l,int r,ll w){mn[k]=w;sum[k]=(r-l+1)*w;laz[k]=w;}inline void pushdown(int k,int l,int r){ll o=laz[k];laz[k]=0;if(!o) return;add(ls,l,mid,o);add(rs,mid+1,r,o);return;}void change(int k,int l,int r,int x,int y,ll w){if(x<=l&&r<=y){add(k,l,r,w);return;}pushdown(k,l,r);if(x<=mid) change(ls,l,mid,x,y,w);if(y>mid) change(rs,mid+1,r,x,y,w);pushup(k);}ll asksum(int k,int l,int r,int x,int y){if(x<=l&&r<=y) return sum[k];pushdown(k,l,r);ll res(0);if(x<=mid) res+=asksum(ls,l,mid,x,y);if(y>mid) res+=asksum(rs,mid+1,r,x,y);return res;}int find(int k,int l,int r,int w){//last pl that val<wif(l==r) return l;pushdown(k,l,r);if(mn[rs]<w) return find(rs,mid+1,r,w);else return find(ls,l,mid,w);}inline ll Asksum(int l,int r){return asksum(1,0,n,l,r);}inline int Find(int w){return find(1,0,n,w);}inline void update(int pl,int w){if(mn[1]>w) return;int ed=Find(w);if(pl<=ed) change(1,0,n,pl,ed,w); return;}
}tr[N];int w[N];
int len[N],dep[N],top[N],son[N],pl[N][20],mx[N][20];
void dfs(int x,int f){dep[x]=dep[f]+1;len[x]=1;if(f){pl[x][0]=f;mx[x][0]=w[f];for(int k=1;pl[x][k-1];k++){pl[x][k]=pl[pl[x][k-1]][k-1];mx[x][k]=max(mx[x][k-1],mx[pl[x][k-1]][k-1]);}}for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;dfs(to,x);if(len[to]+1>len[x]){son[x]=to;len[x]=len[to]+1;}}return;
}
inline int add(int x){return dep[x]-dep[top[x]];
}vector<int>v[N];
ll d[N],ans[N];void solve(int x){if(x==top[x]){tr[x].init(len[x]);}if(son[x]){top[son[x]]=top[x];solve(son[x]);}for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==son[x]) continue;top[to]=to;solve(to);for(int i=0;i<len[to];i++){tr[top[x]].update(i+1+add(x),tr[to].Asksum(i,i));}}for(int id:v[x]){int pl=tr[top[x]].Find(d[id])+1;ans[id]+=(pl-add(x))*d[id];if(add(x)<pl) ans[id]-=tr[top[x]].Asksum(add(x),pl-1);}//debug("x=%d top=%d\n",x,top[x]);tr[top[x]].update(add(x),w[x]);
}inline int calc(int x,int y){int ans(0);for(int k=18;k>=0;k--){if(dep[pl[x][k]]<=dep[y]) continue;ans=max(ans,mx[x][k]);x=pl[x][k];}return ans;
}int main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();for(int i=1;i<=n;i++) w[i]=read();for(int i=2;i<=n;i++) addline(read(),i);dfs(1,0);m=read();for(int i=1;i<=m;i++){int s=read(),t=read();ans[i]+=dep[t]-dep[s];v[s].push_back(i);d[i]=calc(t,s);}top[1]=1;solve(1);for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}