这个题可以说是LNOI2014 LCA这个题的加强版
所以那个题的博客就咕咕咕啦
hhh
qwq直接介绍做法了
考虑一个如何求两个点 x , y x,y x,y的 l c a lca lca到根的路径和,我们发现,如果我们在确定一个根后,将每一条边的边权赋值给他的 t o to to,那么实际上就是对于 x x x,把它所有到根的路径上所有点都标记 + 1 +1 +1,然后在 y y y点统计 y y y到根的路径上所有点的 标 记 数 × 权 值 标记数\times 权值 标记数×权值
那么我们会想到一个做法,考虑主席树,对于第 i i i颗线段树来说,他可以从第 i − 1 i-1 i−1颗线段树的基础上,修改 i i i到根的路径的标记得到。
那么我们对于一次询问的话,我们可以用第 r r r颗线段树减去第 l − 1 l-1 l−1颗线段树,得到一个新的线段树,然后直接对这个东西利用树剖,求出 u u u到根的路径上的 标 记 × 权 值 标记\times 权值 标记×权值,而这里具体实现的话,我们可以直接维护区间和(相当于修改标记也是修改区间和),然后计算的时候,为了方便,我们可以分别在两棵树上进行计算答案,然后做减法。
那么现在问题就转化成了
我们该如何实现一颗主席树的区间修改呢?
这里我们运用一个标记永久化的思想,就是在我们 u p d a t e update update的时候,我们会修改沿途每一个经过的节点的 s u m sum sum(这里注意要维护),并且每走到一个被修改区间包含的区间,就修改一个 a d d add add标记(+1),然后直接 r e t u r n return return,不递归下去。
而询问的时候呢,我们会记录一下沿途的 a d d add add标记之和,到了一个被包含的区间之后,就 r e t u r n return return a d d ∗ g e t s u m ( l , r ) + t [ r o o t ] . s u m add*getsum(l,r)+t[root].sum add∗getsum(l,r)+t[root].sum
这里 g e t s u m getsum getsum是一个边权转化成点权之后的前缀和
这样就可以实现区间修改了
不过其实细节还是很多的
具体的细节就直接看代码吧
// luogu-judger-enable-o2
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
const int maxm = 2*maxn;
int point[maxn],nxt[maxm],to[maxm];
ll cost[maxm];
int cnt,n,m;
struct Node{
int l,r;
ll sum,add;
};
Node t[maxn*100];
ll sum[maxn];
int top[maxn],newnum[maxn],size[maxn],deep[maxn],fa[maxn],son[maxn];
int tmp,tot;
ll val[maxn];
ll mx;
int x[maxn];
ll newval[maxn];
int root[maxn];
int q;
ll dis[maxn],dsum[maxn];
ll getsum(int l,int r)
{
return sum[r]-sum[l-1];
}
struct pp
{
int num,val;
};
pp a[maxn];
void addedge(int x,int y,int w)
{
nxt[++cnt]=point[x];
to[cnt]=y;
cost[cnt]=w;
point[x]=cnt;
}
void dfs1(int x,int faa,int dep)
{
size[x]=1;
deep[x]=dep;
int maxson = -1;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==faa) continue;
dis[p]=dis[x]+cost[i];
dfs1(p,x,dep+1);
val[p]=cost[i];
size[x]+=size[p];
fa[p]=x;
if (maxson<size[p])
{
maxson=size[p];
son[x]=p;
}
}
}
void dfs2(int x,int chain)
{
top[x]=chain;
newnum[x]=++tmp;
newval[tmp]=val[x];
//cout<
if (!son[x]) return;
dfs2(son[x],chain);
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (!newnum[p]) dfs2(p,p);
}
}
int build(int l,int r)
{
int now = ++tot;
if (l==r) return now;
int mid = l+r >> 1;
t[now].l=build(l,mid);
t[now].r=build(mid+1,r);
return now;
}
bool cmp(pp a,pp b)
{
return a.val<b.val;
}
int modify(int pre,int l,int r,int x,int y)
{
// cout<
int now = ++tot;
t[now]=t[pre];
if (x<=l && r<=y)
{
t[now].add++;
return now;
}
int mid = l+r >> 1;
t[now].sum+=max(getsum(max(x,l),min(r,y)),0ll);
if (x<=mid) t[now].l=modify(t[pre].l,l,mid,x,y);
if (y>mid) t[now].r=modify(t[pre].r,mid+1,r,x,y);
return now;
}
ll query(int root,int l,int r,int x,int y,ll add)
{
if (x<=l && r<=y) return (add+t[root].add)*getsum(l,r)+t[root].sum;
int mid = l+r >> 1;
ll ans=0;
if (x<=mid) ans=ans+query(t[root].l,l,mid,x,y,add+t[root].add);
if (y>mid) ans=ans+query(t[root].r,mid+1,r,x,y,add+t[root].add);
return ans;
}
void treeadd(int now,int x,int y)
{
while (top[x]!=top[y])
{
if (deep[top[x]]<deep[top[y]]) swap(x,y);
//cout<
root[now]=modify(root[now],1,n,newnum[top[x]],newnum[x]);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
//cout<
root[now]=modify(root[now],1,n,newnum[x],newnum[y]);
}
ll treesum(int now,int x,int y)
{
ll ans=0;
while (top[x]!=top[y])
{
if (deep[top[x]]<deep[top[y]]) swap(x,y);
ans=ans+query(root[now],1,n,newnum[top[x]],newnum[x],0);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
ans=ans+query(root[now],1,n,newnum[x],newnum[y],0);
return ans;
}
int main()
{
n=read();q=read();mx=read();
for (int i=1;i<=n;i++) a[i].val=read(),a[i].num=i,x[i]=a[i].val;
sort(x+1,x+1+n);
sort(a+1,a+1+n,cmp);
for (int i=1;i<n;i++)
{
int x=read(),y=read(),w=read();
addedge(x,y,w);
addedge(y,x,w);
//cout<
}
dfs1(1,0,0);
dfs2(1,1);
root[0]=build(1,n);
//return 0;
for (int i=1;i<=n;i++) sum[i]=sum[i-1]+newval[i];
for (int i=1;i<=n;i++) dsum[i]=dsum[i-1]+dis[a[i].num];
for (int i=1;i<=n;i++)
{
root[i]=root[i-1];
treeadd(i,1,a[i].num);
//cout<
}
//cout<<"*****"<
ll lastans=0;
for (int i=1;i<=q;i++)
{
ll u=read(),a=read(),b=read();
ll l=min((a+lastans)%mx,(b+lastans)%mx);
ll r=max((a+lastans)%mx,(b+lastans)%mx);
l=lower_bound(x+1,x+1+n,l)-x;
r=upper_bound(x+1,x+1+n,r)-x-1;
// cout<
ll f1 = treesum(l-1,1,u);
ll f2 = treesum(r,1,u);
ll len = r-l+1;
lastans=dis[u]*len + dsum[r]-dsum[l-1]-2*(f2-f1);
cout<<lastans<<"\n";
}
return 0;
}