#长链剖分,启发式合并,二分,队列#UOJ 284 快乐游戏鸡
分析
设 \(s\) 到 \(t\) 的路径(不包含端点)最大的 \(w\) 为 \(mx\),
那么最好的办法就是从 \(s\) 往子树走(不必往 \(t\) 走)先死亡 \(mx\) 次再一步到位走到 \(t\)
那么维护 \(dp[x][i]\) 表示从 \(x\) 开始走第 \(i\) 次死需要走多少步,
虽然 \(i\) 很大,但是其实是很多个段的拼接,反过来用队列记录这些关键的 \(i\)
需要将子节点的 \(i\) 拼接到当前节点,而队列的长度取决于到叶子节点的最长链,
因此进行长链剖分,将轻边合并到重链上,询问时二分最小的 \(i\) 能框住 \(mx\),在队列里记录后缀和来计算答案。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=300011; struct node{int y,next;}e[N],E[N]; typedef long long lll;
int d[N],dep[N],f[N][20],g[N][20],as[N],big[N],Top[N],n,Q,bs[N];
int q[N],_q[N],head[N],tail[N],dfn[N],tot; lll suf[N],ans[N],a[N];
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void print(lll ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void dfs1(int x,int fa){
d[x]=dep[x]=dep[fa]+1,f[x][0]=fa,g[x][0]=a[x];
for (int i=0;f[x][i];++i)
f[x][i+1]=f[f[x][i]][i],g[x][i+1]=max(g[x][i],g[f[x][i]][i]);
for (int i=as[x];i;i=e[i].next){
dfs1(e[i].y,x);
if (d[e[i].y]>d[x]) d[x]=d[e[i].y],big[x]=e[i].y;
}
}
lll query(int x,int y){
if (head[x]>tail[x]) return 0;
int mx=0,fa=f[y][0];
for (int i=18;~i;--i)
if (dep[f[fa][i]]>=dep[x]) mx=max(mx,g[fa][i]),fa=f[fa][i];
if (mx<a[q[head[x]]]) return 1ll*(dep[q[head[x]]]-dep[x])*mx;
int l=head[x],r=tail[x];
while (l<r){
int mid=(l+r)>>1;
if (mx<=a[q[mid]]) r=mid;
else l=mid+1;
}
return suf[head[x]]-suf[l]+a[q[head[x]]]*dep[q[head[x]]]-a[q[l]]*dep[q[l]]+1ll*(dep[q[l]]-dep[x])*mx;
}
void Push(int x,int y){
while (head[x]<=tail[x]&&a[q[head[x]]]<=a[y]) ++head[x];
if (head[x]>tail[x]||dep[q[head[x]]]>dep[y]){
suf[head[x]-1]=0;
if (head[x]<=tail[x]) suf[head[x]-1]=suf[head[x]]+(a[q[head[x]]]-a[y])*dep[q[head[x]]];
q[--head[x]]=y;
}
}
void Merge(int x,int y){
int HEAD=1,TAIL=0;
while (head[x]<=tail[x]&&dep[q[head[x]]]<dep[q[tail[y]]]) _q[++TAIL]=q[head[x]++];
while (HEAD<=TAIL&&head[y]<=tail[y])
if (dep[_q[TAIL]]>dep[q[tail[y]]]) Push(x,_q[TAIL--]);
else Push(x,q[tail[y]--]);
while (HEAD<=TAIL) Push(x,_q[TAIL--]);
while (head[y]<=tail[y]) Push(x,q[tail[y]--]);
}
void dfs2(int x){
dfn[x]=++tot;
if (big[x]) dfs2(big[x]),head[x]=head[big[x]],tail[x]=tail[big[x]];
else head[x]=tot+1,tail[x]=tot;
for (int i=as[x];i;i=e[i].next)
if (e[i].y!=big[x]) dfs2(e[i].y),Merge(x,e[i].y);
for (int i=bs[x];i;i=E[i].next)
ans[i]=query(x,E[i].y)+dep[E[i].y]-dep[x];
Push(x,x);
}
int main(){
n=iut();
for (int i=1;i<=n;++i) a[i]=iut();
for (int i=2;i<=n;++i){
int x=iut();
e[i]=(node){i,as[x]},as[x]=i;
}
Q=iut();
for (int i=1;i<=Q;++i){
int x=iut(),y=iut();
E[i]=(node){y,bs[x]},bs[x]=i;
}
dfs1(1,0),dfs2(1);
for (int i=1;i<=Q;++i) print(ans[i]),putchar(10);
return 0;
}

浙公网安备 33010602011771号