正题
题目链接:https://uoj.ac/problem/284
题目大意
nnn个点的一棵树,每个点有一个wiw_iwi表示至少死亡wiw_iwi次才能通过这个点,否则就会死亡。只能往子节点走,mmm此询问从sis_isi走到tit_iti至少要死多少次。
解题思路
也就算我们要尽早让死亡次数到达s−>ts->ts−>t路径上的最大值mxmxmx。
考虑比较朴素的做法,设fx,if_{x,i}fx,i表示节点xxx的子树中与它距离不超过iii的节点中wiw_iwi的最大值。那么我们处理完这个数组后对于每个询问可以二分到一个ddd使得fx,d≥wif_{x,d}\geq w_ifx,d≥wi然后答案就是mx∗d−∑i=1d−1fx,imx*d-\sum_{i=1}^{d-1}f_{x,i}mx∗d−∑i=1d−1fx,i
具体原理就是先默认每次死亡都要到ddd那么远,然后中间的一些值可以减去。
那么考虑如何快速计算fff,因为它与深度有关,所以考虑长链剖分。fx,if_{x,i}fx,i是可以继承fy,i+1f_{y,i+1}fy,i+1的,但是插入时我们发现这是一个让后缀取maxmaxmax的操作,因为fx,if_{x,i}fx,i是随着iii单调递增的,所以我们可以使用线段树。用线段树上二分+区间修改和求和就可以快速计算fff和计算答案。
世界复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define dis(x) (dep[x]-dep[top[x]])
using namespace std;
const ll N=3e5+10,T=18,M=N<<4;
struct node{ll to,next;
}a[N<<1];
ll n,m,tot,c[N],ls[N],len[N],dep[N],top[N];
ll cnt,son[N],d[N],ans[N],f[N][T+1],g[N][T+1];
vector<int> q[N];
struct Seq_Tree{ll n;vector<ll> w,v,lazy;void init(ll L){w.resize(L<<2,0);v.resize(L<<2,0);lazy.resize(L<<2,0);n=L-1;return;}void Downdata(ll x,ll l,ll r){if(!lazy[x])return;ll mid=(l+r)>>1;lazy[x*2]=lazy[x*2+1]=lazy[x];v[x*2]=v[x*2+1]=lazy[x];w[x*2]=(mid-l+1)*lazy[x];w[x*2+1]=(r-mid)*lazy[x];lazy[x]=0;return;}void Change(ll x,ll L,ll R,ll l,ll r,ll val){if(L==l&&R==r){lazy[x]=v[x]=val;w[x]=(R-L+1)*val;return;}ll mid=(L+R)>>1;Downdata(x,L,R);if(r<=mid)Change(x*2,L,mid,l,r,val);else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);w[x]=w[x*2]+w[x*2+1];v[x]=max(v[x*2],v[x*2+1]);return;}ll Find(ll x,ll l,ll r,ll k){if(v[x]<k)return n+1; if(l==r)return l;ll mid=(l+r)>>1;Downdata(x,l,r);if(v[x*2]>=k)return Find(x*2,l,mid,k);return Find(x*2+1,mid+1,r,k);}void Insert(ll x,ll w){ll y=Find(1,0,n,w);if(y>x)Change(1,0,n,x,y-1,w);return;}ll Ask(ll x,ll L,ll R,ll l,ll r){if(L==l&&R==r)return w[x];ll mid=(L+R)>>1;Downdata(x,L,R);if(r<=mid)return Ask(x*2,L,mid,l,r);if(l>mid)return Ask(x*2+1,mid+1,R,l,r);return Ask(x*2,L,mid,l,mid)+Ask(x*2+1,mid+1,R,mid+1,r);}ll Query(ll l,ll r){return Ask(1,0,n,l,r);}
}t[N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void init(){for(ll j=1;j<=T;j++)for(ll i=1;i<=n;i++){f[i][j]=f[f[i][j-1]][j-1];g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);}
}
ll get_max(ll s,ll t){ll w=0;for(ll i=T;i>=0;i--)if(dep[f[t][i]]>dep[s])w=max(w,g[t][i]),t=f[t][i];return w;
}
void dfs(ll x){len[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;dfs(y);len[x]=max(len[x],len[y]+1);if(len[y]>len[son[x]])son[x]=y;}return;
}
void solve(ll x){if(top[x]==x)t[x].init(len[x]);if(son[x]){top[son[x]]=top[x];solve(son[x]);}for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==son[x])continue;top[y]=y;solve(y);for(ll j=0;j<len[y];j++)t[top[x]].Insert(dis(x)+j+1,t[y].Query(j,j));}for(ll i=0;i<q[x].size();i++){ll p=q[x][i],k=top[x];ll pos=t[k].Find(1,0,len[k]-1,d[p]);if(pos>dis(x))ans[p]-=t[k].Query(dis(x),pos-1);ans[p]+=d[p]*(pos-dis(x));}t[top[x]].Insert(dis(x),c[x]);return;
}
int main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld",&c[i]);dep[1]=1;for(ll i=2;i<=n;i++){scanf("%lld",&f[i][0]);dep[i]=dep[f[i][0]]+1;g[i][0]=c[f[i][0]];addl(f[i][0],i);}init();scanf("%lld",&m);for(ll i=1;i<=m;i++){ll s,t;scanf("%lld%lld",&s,&t);ans[i]=dep[t]-dep[s];d[i]=get_max(s,t);q[s].push_back(i);}dfs(1);top[1]=1;solve(1);for(ll i=1;i<=m;i++)printf("%lld\n",ans[i]);return 0;
}