题目大意
一棵树,有n个节点,边有权值
有
∑x on pathae(x)
1≤n,q≤105,ai≤103,li,l≤109
题目分析
乍一看题意很绕,无从下手。考虑到有边权大小的限制,如果在线可能很难处理(函数式线段树???)。
那我们就离线,把边和询问放一块儿降序排序,按顺序处理询问以及插入,那么已经插入的边就是超过询问的lim的。
树剖维护区间左右两段连续的有边的长度以及答案,然后合并一下即可。
细节见代码实现。
时间复杂度O((n+q)log2n)。
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=100050;
const int M=N<<1;
const int Q=100050;
const int S=N+Q;
const int EL=N<<1;
const int LGEL=18;
int last[N],fa[N],high[N],pos[N],a[N],hea[N],size[N],DFN[N],prt[N];
int n,q,tot,el,idx,lgel,s;
int tov[M],next[M];
int rmq[EL][LGEL];
int euler[EL];
int ans[Q];
struct D
{
int u,v,li,og;
}op[S];
bool operator<(D x,D y){return x.li>y.li||x.li==y.li&&x.og>y.og;}
struct L
{
int l,r,ans,size;
L (int l0=0,int r0=0,int ans0=0,int size0=0){l=l0,r=r0,ans=ans0,size=size0;}
};
L operator+(L x,L y)
{
L ret;
ret.size=x.size+y.size;
ret.l=x.l+(x.l==x.size?y.l:0);
ret.r=y.r+(y.r==y.size?x.r:0);
ret.ans=x.ans+y.ans-a[x.r]-a[y.l]+a[x.r+y.l];
return ret;
}
struct segment_tree
{
L v[N<<2];
void build(int x,int l,int r)
{
if (l==r)
{
v[x].size=1;
return;
}
int mid=l+r>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
v[x].size=v[x<<1].size+v[x<<1|1].size;
}
void modify(int x,int y,int l,int r)
{
if (l==r)
{
v[x].ans=a[v[x].l=v[x].r=1];
return;
}
int mid=l+r>>1;
if (y<=mid) modify(x<<1,y,l,mid);
else modify(x<<1|1,y,mid+1,r);
v[x]=v[x<<1]+v[x<<1|1];
}
L query(int x,int st,int en,int l,int r)
{
if (st==l&&en==r) return v[x];
int mid=l+r>>1;
if (en<=mid) return query(x<<1,st,en,l,mid);
else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r);
else return query(x<<1,st,mid,l,mid)+query(x<<1|1,mid+1,en,mid+1,r);
}
}t;
void insert(int x,int y)
{
tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
void dfs(int x)
{
size[euler[++el]=x]=1,pos[x]=el;
for (int i=last[x],y;i;i=next[i])
if ((y=tov[i])!=fa[x])
{
fa[y]=x,high[y]=high[x]+1,dfs(y),size[euler[++el]=x]+=size[y];
if (!hea[x]||size[hea[x]]<size[y]) hea[x]=y;
}
}
void pre()
{
lgel=trunc(log(el)/log(2));
for (int i=1;i<=el;i++) rmq[i][0]=euler[i];
for (int j=1;j<=lgel;j++)
for (int i=1;i+(1<<j)-1<=el;i++)
if (high[rmq[i][j-1]]<high[rmq[i+(1<<j-1)][j-1]])
rmq[i][j]=rmq[i][j-1];
else
rmq[i][j]=rmq[i+(1<<j-1)][j-1];
}
void split(int x,int z)
{
DFN[x]=++idx,prt[x]=z;
if (hea[x]) split(hea[x],z);
else return;
for (int i=last[x],y;i;i=next[i])
if ((y=tov[i])!=fa[x]&&y!=hea[x]) split(y,y);
}
int getrmq(int l,int r)
{
int lgr=trunc(log(r-l+1)/log(2));
if (high[rmq[l][lgr]]<high[rmq[r-(1<<lgr)+1][lgr]]) return rmq[l][lgr];
else return rmq[r-(1<<lgr)+1][lgr];
}
int lca(int x,int y)
{
if ((x=pos[x])>(y=pos[y])) swap(x,y);
return getrmq(x,y);
}
int getans(int x,int y)
{
int z=lca(x,y),f;
L ret1=L(),ret2=L();
for (f=prt[x];f!=prt[z];x=fa[f],f=prt[x]) ret1=t.query(1,DFN[f],DFN[x],1,idx)+ret1;
if (z!=x) ret1=t.query(1,DFN[z]+1,DFN[x],1,idx)+ret1;
for (f=prt[y];f!=prt[z];y=fa[f],f=prt[y]) ret2=t.query(1,DFN[f],DFN[y],1,idx)+ret2;
if (z!=y) ret2=t.query(1,DFN[z]+1,DFN[y],1,idx)+ret2;
return ret1.ans+ret2.ans-a[ret1.l]-a[ret2.l]+a[ret1.l+ret2.l];
}
void change(int x,int y)
{
if (fa[x]!=y) swap(x,y);
t.modify(1,DFN[x],1,idx);
}
int main()
{
freopen("nekopara.in","r",stdin),freopen("nekopara.out","w",stdout);
n=read();
for (int i=1;i<n;i++) a[i]=read();
for (int i=1;i<n;i++)
{
op[i].u=read()+1,op[i].v=read()+1,op[i].li=read();
insert(op[i].u,op[i].v),insert(op[i].v,op[i].u);
}
dfs(1),pre(),split(1,1);
t.build(1,1,idx);
q=read();
for (int i=1;i<=q;i++) op[i+n-1].u=read()+1,op[i+n-1].v=read()+1,op[i+n-1].li=read(),op[i+n-1].og=i;
s=n-1+q;
sort(op+1,op+1+s);
for (int i=1;i<=s;i++)
if (op[i].og)
ans[op[i].og]=getans(op[i].u,op[i].v);
else
change(op[i].u,op[i].v);
for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
fclose(stdin),fclose(stdout);
return 0;
}