题目大意:给出一棵边带权的树(每个点的度小于等于3),每个点都带有一个年龄值v,强制在线询问年龄值在[l,r]区间内的所有点到指定点u的距离和。
这道题有两种做法,两种做法都比较有代表性。
第一种是树链剖分加可持久化线段树,我们先考虑简化问题,假设我们忽略年龄值,而只是在线询问所有点到一个点的距离和呢?转换一下,设dep(u)代表u节点到根的距离和,那么答案就等于n*dep(u)+所有点到根的距离(这两者都可以预处理)-LCA,即我们将求每对点对的距离和的问题转化为求所有点与点u的LCA的dep之和,然而仅仅只是这样我们依然无法解决这个问题,这个时候,我们就需要换位思考,我们之前想的是正着求所有LCA的dep之和,现在我们反过来,我们求哪些边的权值会被计算到LCA的dep里面,我们发现,LCA只可能是u以及u的祖先,能够被计算为LCA的dep的边权也只会是连接这些点的边,那么每条边会被计算多少次呢?很明显,这条边有多少个儿子就被计算了多少次,而总共的LCA的dep之和就是所有的u的祖先边的权值乘上儿子个数的和,如果题目就到这里,那么我们只需要通过两遍DFS的预处理就可以直接O(1)回答我们的问题了,然而这道题却偏偏设置了一个年龄的区间限制,那么我们首先可以想到差分,然后我们将所有点按照年龄排序,一个点一个点地添加进去,每个点会对他的所有祖先边贡献等同于他的祖先边的权值,这满足叠加性,所以我们可以使用树剖来完成这个过程,然后把树剖用的线段树做成可持久化,就可以使用差分了。这种方法是非常快的,但是转换起来有些困难。
第二种是动态树分治,这是一种比较直观且非常暴力的方法,首先我们考虑,假如这个问题是离线的,那么我们完全可以使用点分治直接搞定(加一个小小的容斥)。那么只有一个询问呢?虽然我们可以直接DFS,O(n)解决——这个不在考虑范围之内,但是我们也可以强行犯傻,使用点分治来求,由于每个点的度小于等于3,这给了我们以方便,我们对于每个重心,先找到我们要求的那个点所在的子树,然后再遍历其余的最多2个子树,对每个子树进行排序之后,算出前缀和然后差分加二分,而对于强制的在线询问来说,我们只需要提前将点分治的那个过程记录下来,往细了说就是,记录下一个点的所有父亲(此父亲非彼父亲,其意思那些从它出发能够找到这个点的所有重心),并记录下这个点属于它的父亲的哪个子树,并记下这个点到它的父亲的距离,然后记录下在点分治过程中所有重心的子树(按照年龄排序并前缀和处理好),做了这些处理之后,我们就可以来在线询问了,对于某个制定的点,我们取出它的所有父亲,由于我们知道它在它的父亲中的哪个子树中,所以我们可以调出另外两个子树,二分处理取前缀和做差分,然后由于最多只有log层,所以总的时间复杂度为n(log2n)^2,这个复杂度看起来和前面的那个一样,但是太暴力了,常数很大,不做点常数优化是过不了OJ的,而且很难写……当然,第一种方法也不算好写。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; int n,q,A,cnt=0,root=0,sum; int first[150005]={0}; const int INF=0x3f3f3f3f; long long ans=0; struct Edge { int next,to,v; }edge[300005]; struct Son { long long dis; int age; void Insert(long long x,int y){dis=x;age=y;} }; struct Fa { int dis,top,num; void Insert(int x,int y,int z){dis=x;top=y;num=z;} }; struct Tree { int age,son,f; bool vis; vector<Son>sons[3]; vector<Fa>fa; }tree[150005]; inline void add(int x,int y,int v) { cnt++; edge[cnt].to=y; edge[cnt].v=v; edge[cnt].next=first[x]; first[x]=cnt; } void Get(int u,int fa) { tree[u].son=1; tree[u].f=0; for(int i=first[u];i;i=edge[i].next) { int v=edge[i].to; if(v==fa||tree[v].vis)continue; Get(v,u); tree[u].son+=tree[v].son; tree[u].f=max(tree[u].f,tree[v].son); } tree[u].f=max(tree[u].f,sum-tree[u].son); if(tree[u].f<tree[root].f)root=u; } void DFS(int u,int fa,int top,int dis,int num) { Fa temp1; temp1.Insert(dis,top,num); tree[u].fa.push_back(temp1); Son temp2; temp2.Insert(dis,tree[u].age); tree[top].sons[num].push_back(temp2); for(int i=first[u];i;i=edge[i].next) { int v=edge[i].to; if(tree[v].vis||v==fa)continue; DFS(v,u,top,dis+edge[i].v,num); } } void Solve(int x) { int now=0; Fa temp; temp.Insert(0,x,3); tree[x].fa.push_back(temp); tree[x].vis=1; for(int i=first[x];i;i=edge[i].next) { int y=edge[i].to; if(tree[y].vis)continue; DFS(y,x,x,edge[i].v,now++); } for(int i=first[x];i;i=edge[i].next) { int y=edge[i].to; if(tree[y].vis)continue; sum=tree[y].son; root=0; Get(y,x); Solve(root); } } inline bool cmp(Son a,Son b) { return a.age<b.age; } int main(){ scanf("%d%d%d",&n,&q,&A); for(int i=1;i<=n;i++)scanf("%d",&tree[i].age); int x,y,v,u,len,num,L,R,mid; long long suml; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&v); add(x,y,v); add(y,x,v); } sum=n; tree[root].f=INF; Get(1,0); Solve(root); for(int i=1;i<=n;i++) for(int j=0;j<3;j++) { sort(tree[i].sons[j].begin(),tree[i].sons[j].end(),cmp); for(int l=1;l<tree[i].sons[j].size();l++)tree[i].sons[j][l].dis+=tree[i].sons[j][l-1].dis; } ans=0; for(int i=1;i<=q;i++) { scanf("%d%d%d",&v,&x,&y); x=(x+ans)%A; y=(y+ans)%A; ans=0; if(x>y)swap(x,y); for(int j=0;j<tree[v].fa.size();j++) { u=tree[v].fa[j].top; len=tree[v].fa[j].dis; num=tree[v].fa[j].num; if(x<=tree[u].age&&tree[u].age<=y)ans+=len; for(int l=0;l<3;l++) { if(l==num)continue; L=0,R=tree[u].sons[l].size()-1; if(L>R)continue; while(L<R) { mid=(L+R)/2; if(tree[u].sons[l][mid].age<=x-1)L=mid+1; else R=mid; } if(L==0) { if(tree[u].sons[l][L].age>x-1)suml=0,L=-1; else suml=tree[u].sons[l][L].dis,L=0; } else { if(tree[u].sons[l][L].age<=x-1)L++; L--; suml=tree[u].sons[l][L].dis; } ans-=1ll*len*(L+1)+suml; L=0,R=tree[u].sons[l].size()-1; while(L<R) { mid=(L+R)/2; if(tree[u].sons[l][mid].age<=y)L=mid+1; else R=mid; } if(L==0) { if(tree[u].sons[l][L].age>y)suml=0,L=-1; else suml=tree[u].sons[l][L].dis,L=0; } else { if(tree[u].sons[l][L].age<=y)L++; L--; suml=tree[u].sons[l][L].dis; } ans+=1ll*len*(L+1)+suml; } } cout<<ans<<'\n'; } return 0; }