这叫啥?可持久化树链剖分?
首先忽略掉那个年龄的限制
那就是求所有点到某点的路径之和
这个画个图会发现是 Σdepth[i]+n*depth[u]-2*Σdepth(lca(u,i))
前两个可以O(1)求,最后那个可以用树剖求。
方法就是每个点到根的路径测度+1,这个可以用线段树更新,然后u到根的路径上的测度与长度之积的和就是答案了。
但是出现了那个可恶的限制
不过嘛,我们有传说中的chairman algorithm
于是我们把它变成两个前缀相减,再用可持久化线段树求前缀和
空间O(n(logn)^2),时间O(n(logn)^2)
(由于这题空间不是很好说,于是我把pushdown操作干掉了,每次往下找的时候把标记加起来,然后就上首页了,感觉跑得飞快啊)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int M=13000000; const int N=150000+5; typedef long long ll; struct Node{ int lc,rc,cov,ti; ll sum; }tr[M]; int tot; int dep[N],top[N],fa[N],siz[N],son[N],pos[N],rank[N],cost[N],depth[N],sz; struct Edge{int to,next,v;}e[N<<1]; int head[N],cnt; ll len(int l,int r){ return depth[rank[r]]-depth[fa[rank[l]]]; } void ins(int u,int v,int w){ e[++cnt]=(Edge){v,head[u],w};head[u]=cnt; } void dfs(int u){ siz[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to;if(v==fa[u])continue; fa[v]=u;dep[v]=dep[u]+1;cost[v]=e[i].v;depth[v]=depth[u]+cost[v]; dfs(v); siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v; } } void dfs(int u,int tp){ top[u]=tp;pos[u]=++sz;rank[sz]=u; if(son[u])dfs(son[u],tp); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v!=son[u]&&v!=fa[u])dfs(v,v); } } ll update(int &o,int l,int r,int a,int b,int ti){ if(tr[o].ti!=ti)tr[++tot]=tr[o],o=tot,tr[tot].ti=ti; if(l==a&&r==b){tr[o].cov++;tr[o].sum+=len(l,r);return len(l,r);} else{ int mid=l+r>>1; ll add=0; if(b<=mid)add+=update(tr[o].lc,l,mid,a,b,ti); else if(mid<a)add+=update(tr[o].rc,mid+1,r,a,b,ti); else add+=update(tr[o].lc,l,mid,a,mid,ti)+update(tr[o].rc,mid+1,r,mid+1,b,ti); tr[o].sum+=add; return add; } } ll query(int o,int l,int r,int a,int b,int cov){ if(l==a&&b==r)return tr[o].sum+len(l,r)*cov; else{ int mid=l+r>>1; cov+=tr[o].cov; if(b<=mid)return query(tr[o].lc,l,mid,a,b,cov); else if(mid<a)return query(tr[o].rc,mid+1,r,a,b,cov); else return query(tr[o].lc,l,mid,a,mid,cov)+query(tr[o].rc,mid+1,r,mid+1,b,cov); } } int root[N]; int n; void update(int u,int ti){ while(u){ update(root[ti],1,n,pos[top[u]],pos[u],ti); u=fa[top[u]]; } } ll query(int u,int ti){ ll ans=0; while(u){ ans+=query(root[ti],1,n,pos[top[u]],pos[u],0); u=fa[top[u]]; } return ans; } int hash[N],id[N],rk[N]; ll sum[N]; bool cmp(int i,int j){ return hash[i]<hash[j]; } int findl(int l,int r,int x){ while(l<=r){ int mid=l+r>>1; if(hash[id[mid]]<x)l=mid+1; else r=mid-1; } return l-1; } int findr(int l,int r,int x){ while(l<=r){ int mid=l+r>>1; if(hash[id[mid]]<=x)l=mid+1; else r=mid-1; } return l-1; } ll ask(int u,int ti){ return sum[ti]+(ll)ti*depth[u]-2*query(u,ti); } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int q,A;scanf("%d%d%d",&n,&q,&A); for(int i=1;i<=n;i++){ scanf("%d",&hash[i]); id[i]=i; } sort(id+1,id+1+n,cmp); for(int i=1;i<n;i++){ int u,v,w;scanf("%d%d%d",&u,&v,&w); ins(u,v,w);ins(v,u,w); } dfs(1);dfs(1,1); for(int i=1;i<=n;i++){ root[i]=root[i-1]; update(id[i],i); rk[id[i]]=i; sum[i]=sum[i-1]+depth[id[i]]; } ll ans=0; while(q--){ int u;ll a,b;scanf("%d%lld%lld",&u,&a,&b); int l=(a+ans)%A,r=(b+ans)%A; if(l>r)swap(l,r); l=findl(1,n,l);r=findr(1,n,r); ans=ask(u,r)-ask(u,l); printf("%lld\n",ans); } return 0; }这题好像能用动态点分治做哎,不过我比较懒(明明动态点分治更好写好吗)