BZOJ 4012 HNOI2015 开店 树的边分治

Posted KKKorange的代码盒子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4012 HNOI2015 开店 树的边分治相关的知识,希望对你有一定的参考价值。

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012

 

题意概述:给出一颗N点的树,保证树上所有点的度不超过3,树上每个点有权值,每条边有权值,现在有Q组询问,每组给出信息u,L,R,问点权在区间[L,R]的点到点u的距离和为多少。强制在线。

N<=150000,Q<=200000.

 

可能这是我这几天做过的题里面最水但是最码的一个了。。。。

实际上看见树上所有点的度不超过3就感觉可以用边分治做,具体的思路实际上很简单,建立一个边分治结构,这个结构大约有logN层,每层有N个点,对于每组询问,看询问的点u在当前树被分成的两棵子树中的哪一棵,统计出另一个子树中所有点权在询问区间内的点到u的距离,然后递归到u所在的子树,最终就可以计算出答案。

重点在于实现,一不小心处理的时候就会多写出几个常数出来。。。(不要问我是怎么知道的)

实际上这个题我yy的东西重点就在于把边分治建立成一个二叉树的结构然后在里面去搞事情(小火车万岁!),最多有2N个分治结构(每条边会产生2个,有N-1条边)。每一层每个点只会有一个信息,利用这个性质可以记录很多东西,询问的时候在分治结构中递归,令当前分治结构中询问点所在的子树根节点为x,另一边的子树根节点为y(实际上就是断掉的边的两个端点),每一次询问的时候就在y的信息里面二分计算询问区间内点到y的距离,同时得到这些点的数量,然后把从y经过x到u的距离补全累加到答案中,递归。(要点已经交代完了,剩下的请各位自己yy,手动滑稽)

预处理可以用归并排序做到O(nlogn),主要复杂度在查询,单次是O(log2n)。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<set>
  9 #include<map>
 10 #include<vector>
 11 #include<cctype>
 12 using namespace std;
 13 const int maxn=150005;
 14 const int maxd=45;
 15 typedef long long LL;
 16 
 17 int N,Q,A,L,R,age[maxn]; LL ans,dist[maxd][maxn];
 18 struct edge{ int to,next,w; }E[maxn<<1];
 19 struct data{ int id,a; LL b; }D[maxd][maxn]; int cnt;
 20 int first[maxn],np,sz[maxn],rt[maxd][maxn];
 21 int ke[maxn<<1],MAX,ch[maxn<<1][2],l[maxn<<1],r[maxn<<1],size[maxd],tot[maxn<<1];
 22 bool vis[maxn<<1];
 23 
 24 void _scanf(int &x)
 25 {
 26     x=0;
 27     char cha=getchar();
 28     while(cha<0||cha>9) cha=getchar();
 29     while(cha>=0&&cha<=9) x=x*10+cha-0,cha=getchar();
 30 }
 31 int out_cnt,out[22];
 32 void _printf(LL x)
 33 {
 34     out_cnt=0;
 35     out[++out_cnt]=x%10,x/=10;
 36     while(x) out[++out_cnt]=x%10,x/=10;
 37     while(out_cnt) putchar(0+out[out_cnt--]);
 38 }
 39 void add_edge(int u,int v,int w)
 40 {
 41     E[++np]=(edge){v,first[u],w};
 42     first[u]=np;
 43 }
 44 void data_in()
 45 {
 46     _scanf(N);_scanf(Q);_scanf(A);
 47     for(int i=1;i<=N;i++) _scanf(age[i]);
 48     int x,y,z;
 49     for(int i=1;i<N;i++){
 50         _scanf(x);_scanf(y);_scanf(z);
 51         add_edge(x,y,z);
 52         add_edge(y,x,z);
 53     }
 54 }
 55 void DFS1(int i,int f,int SZ,int id)
 56 {
 57     sz[i]=1;
 58     for(int p=first[i];p;p=E[p].next){
 59         int j=E[p].to;
 60         if(j==f||vis[p]) continue;
 61         DFS1(j,i,SZ,id);
 62         sz[i]+=sz[j];
 63         int tmp=max(sz[j],SZ-sz[j]);
 64         if(tmp<MAX) MAX=tmp,ke[id]=p;
 65     }
 66 }
 67 void DFS2(int i,int f,int d,LL l)
 68 {
 69     size[d]++,dist[d][i]=l;
 70     for(int p=first[i];p;p=E[p].next){
 71         int j=E[p].to;
 72         if(j==f||vis[p]) continue;
 73         rt[d][j]=rt[d][i];
 74         DFS2(j,i,d,l+E[p].w);
 75     }
 76 }
 77 bool cmp(data x,data y) { return x.a<y.a; }
 78 void merge_sort(int x,int d)
 79 {
 80     int pos=l[x],i=l[ch[x][0]],j=r[ch[x][0]],r1=r[ch[x][0]],r2=r[ch[x][1]];
 81     while(i<r1&&j<r2) D[d][pos++]=cmp(D[d+1][i],D[d+1][j])?D[d+1][i++]:D[d+1][j++];
 82     while(i<r1) D[d][pos++]=D[d+1][i++];
 83     while(j<r2) D[d][pos++]=D[d+1][j++];
 84 }
 85 void div_tree(int i,int SZ,int id,int d)
 86 {
 87     if((tot[id]=SZ)<=1) return;
 88     MAX=SZ; DFS1(i,0,SZ,id);
 89     vis[ke[id]]=vis[(ke[id]-1^1)+1]=1;
 90     int o[2]={E[ke[id]].to,E[(ke[id]-1^1)+1].to},_sz[2]={sz[o[0]],SZ-sz[o[0]]};
 91     div_tree(o[0],_sz[0],ch[id][0]=++cnt,d+1);
 92     div_tree(o[1],_sz[1],ch[id][1]=++cnt,d+1);
 93     for(int k=0;k<2;k++){
 94         int to=ch[id][k];
 95         l[to]=size[d],rt[d][o[k]]=o[k];
 96         DFS2(o[k],0,d,0);
 97         r[to]=size[d];
 98         if(_sz[k]>1) merge_sort(to,d);
 99         else D[d][l[to]]=(data){o[k],age[o[k]],0};
100         D[d][l[to]].b=dist[d][D[d][l[to]].id];
101         for(int j=l[to]+1;j<r[to];j++)
102             D[d][j].b=dist[d][D[d][j].id]+D[d][j-1].b;
103     }
104     vis[ke[id]]=vis[(ke[id]-1^1)+1]=0;
105 }
106 void solve(int p,int id,int d)
107 {
108     while(tot[id]>1){
109         int x=E[ke[id]].to,y=E[(ke[id]-1^1)+1].to;
110         int dd=rt[d][p]!=x,t=ch[id][dd^1];
111         data tmp;
112         tmp=(data){0,L,0};
113         int ll=lower_bound(D[d]+l[t],D[d]+r[t],tmp,cmp)-D[d]-1;
114         LL v1=ll==l[t]-1?0:D[d][ll].b;
115         tmp=(data){0,R,0};
116         int rr=upper_bound(D[d]+l[t],D[d]+r[t],tmp,cmp)-D[d]-1;
117         LL v2=rr==l[t]-1?0:D[d][rr].b;
118         ans+=v2-v1+(rr-ll)*(E[ke[id]].w+dist[d][p]);
119         id=ch[id][dd],d++;
120     }
121 }
122 void work()
123 {
124     div_tree(1,N,++cnt,1);
125     int u,a,b;
126     for(int i=1;i<=Q;i++){
127         _scanf(u);_scanf(a);_scanf(b);
128         L=min((a+ans)%A,(b+ans)%A);
129         R=max((a+ans)%A,(b+ans)%A);
130         ans=0;
131         solve(u,1,1);
132         _printf(ans),putchar(\n);
133     }
134 }
135 int main()
136 {
137     data_in();
138     work();
139     return 0;
140 }

 

以上是关于BZOJ 4012 HNOI2015 开店 树的边分治的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4012: [HNOI2015]开店

[BZOJ 4012][HNOI2015]开店(树链剖分+主席树)

bzoj4012[HNOI2015]开店 动态树分治+二分查找

[BZOJ4012][HNOI2015]开店(动态点分治)

bzoj4012 HNOI2015—开店

BZOJ 4012 HNOI2015 开店