HNOI2015 开店

来源:互联网 发布:驱动精灵mac版下载 编辑:程序博客网 时间:2024/04/24 18:26

题目大意:给出一棵边带权的树(每个点的度小于等于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;}            

1 0
原创粉丝点击