询问颜色在 [L,R] 区间的点到一个点的距离和,强制在线
这题有两个做法,一个是动态树分治我不会,另一个是树剖
算
u,v
两点在树上的距离我们可以用
u,v
到根的距离和减2倍
LCA(u,v)
到根的距离和,那对于多个
v
的话,可以将每个
那么将这个做法推广一下,对于每种颜色维护一棵可持久化线段树,一个点一个点添进去,询问的话用
code:
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 160000;
struct edge
{
int y,nex;
ll c;
edge(){}
edge(int _y,ll _c,int _nex){y=_y;c=_c;nex=_nex;}
}a[maxn<<1]; int len,fir[maxn];
void ins(int x,int y,ll c){a[++len]=edge(y,c,fir[x]);fir[x]=len;}
struct tree
{
int lc,rc;
ll c,flag;
}tr[maxn*120]; int cnt;
int siz[maxn],son[maxn],fa[maxn],top[maxn],w[maxn];
ll fas[maxn],sumn[maxn],sume[maxn],A;
int _to[maxn],n,m,z;
int root[maxn];
struct node
{
int x,age,k;
ll s;
}s[maxn];
void clear(int x){tr[x].lc=tr[x].rc=tr[x].c=tr[x].flag=0;}
void merge(int &x,int y)
{
if(!x){x=y;return ;}
if(!y)return ;
merge(tr[x].lc,tr[y].lc);
merge(tr[x].rc,tr[y].rc);
tr[x].c+=tr[y].c; tr[x].flag+=tr[y].flag;
return ;
}
void add(int &x,int l,int r,int lx,int rx)
{
if(!x)clear(x=++cnt);
if(lx<=l&&r<=rx)
{
tr[x].c+=sume[r]-sume[l-1];
tr[x].flag++;
return ;
}
int mid=(l+r)>>1;
if(rx<=mid) add(tr[x].lc,l,mid,lx,rx);
else if(lx>mid) add(tr[x].rc,mid+1,r,lx,rx);
else
{
add(tr[x].lc,l,mid,lx,mid);
add(tr[x].rc,mid+1,r,mid+1,rx);
}
tr[x].c+=sume[rx]-sume[lx-1];
}
ll Query(int x,int l,int r,int lx,int rx,ll fl)
{
if(!x)return fl*(sume[rx]-sume[lx-1]);
if(lx<=l&&r<=rx)return tr[x].c+fl*(sume[r]-sume[l-1]);
int mid=(l+r)>>1; fl+=tr[x].flag;
if(rx<=mid)return Query(tr[x].lc,l,mid,lx,rx,fl);
else if(lx>mid) return Query(tr[x].rc,mid+1,r,lx,rx,fl);
else return Query(tr[x].lc,l,mid,lx,mid,fl)+
Query(tr[x].rc,mid+1,r,mid+1,rx,fl);
}
void dfs(int x)
{
siz[x]=1; son[x]=0;
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y!=fa[x])
{
fa[y]=x; fas[y]=a[k].c;
dfs(y); siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
}
}
void build_(int x,int tp)
{
w[x]=++z; sume[z]=sume[z-1]+fas[x]; top[x]=tp;
if(son[x]) build_(son[x],tp);
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y!=fa[x]&&y!=son[x]) build_(y,y);
}
}
int update(int x,int &rt)
{
int ret=0;
while(x)
{
int f1=top[x];
add(rt,1,z,w[f1],w[x]); ret+=sume[w[x]]-sume[w[f1]-1];
x=fa[f1];
}
return ret;
}
int find_l(int x)
{
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(s[mid].age<x)l=mid+1;
else r=mid-1;
}
return l-1;
}
int find_r(int x)
{
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(s[mid].age<=x)l=mid+1;
else r=mid-1;
}
return l-1;
}
ll _Ans(int x,int l,int r)
{
ll s1=0,s2=0;
while(x)
{
int f1=top[x];
s1+=Query(root[l],1,z,w[f1],w[x],0);
s2+=Query(root[r],1,z,w[f1],w[x],0);
x=fa[f1];
}
return s2-s1;
}
ll g_ans(int x,int l,int r)
{
int t1=find_l(l),t2=find_r(r);
if(t1>t2)return 0;
ll s1=sumn[s[t2].k]-sumn[s[t1].k]+(t2-t1)*s[_to[x]].s;
ll s2=2*_Ans(x,s[t1].k,s[t2].k);
return s1-s2;
}
bool cmp(node x,node y){return x.age<y.age;}
int main()
{
memset(fir,0,sizeof fir); len=z=cnt=0;
scanf("%d%d%lld",&n,&m,&A);
for(int i=1;i<=n;i++) {scanf("%d",&s[i].age); s[i].x=i;}
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++)_to[s[i].x]=i;
for(int i=1;i<n;i++)
{
int x,y;ll c; scanf("%d%d%lld",&x,&y,&c);
ins(x,y,c);ins(y,x,c);
}
dfs(1); build_(1,1);
int nowc=-1,k=0;
for(int i=1;i<=n;i++)
{
if(s[i].age!=nowc)
{
if(k)merge(root[k],root[k-1]);
nowc=s[i].age;k++; sumn[k]=sumn[k-1];
}
s[i].k=k;
s[i].s=update(s[i].x,root[k]);
sumn[k]+=s[i].s;
}
merge(root[k],root[k-1]);
ll ans=0;
while(m--)
{
int u;
ll l,r;scanf("%d%lld%lld",&u,&l,&r);
ll L,R;
L=min((l+ans)%A,(r+ans)%A);
R=max((l+ans)%A,(r+ans)%A);
ans=g_ans(u,L,R);
printf("%lld\n",ans);
}
return 0;
}