[HNOI2015]开店
题目描述
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。
这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,其中第 i 个地方的妖怪年龄是 x_i。
妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即年龄大于等于 L、小于等于 R)的妖怪的店。
也有可能 u这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。
幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
输入输出格式
输入格式:第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖怪的年龄上限。 第二行n个用空格分开的数 x_1、x_2、...、x_n,x_i 表示第i 个地点妖怪的年龄,满足0<=x_i<A。(年龄是可以为 0的,例如刚出生的妖怪的年龄为 0。) 接下来 n-1 行,每行三个用空格分开的数 a、b、c,表示树上的顶点 a 和 b 之间有一条权为c(1 <= c <= 1000)的边,a和b 是顶点编号。 接下来Q行,每行三个用空格分开的数 u、 a、 b。对于这 Q行的每一行,用 a、b、A计算出 L和R,表示询问”在地方 u开店,面向妖怪的年龄区间为[L,R]的方案的方便值是多少“。对于其中第 1 行,L 和 R 的计算方法为:L=min(a%A,b%A), R=max(a%A,b%A)。对于第 2到第 Q行,假设前一行得到的方便值为 ans,那么当前行的 L 和 R 计算方法为: L=min((a+ans)%A,(b+ans)%A), R=max((a+ans)%A,(b+ans)%A)。
输出格式:对于每个方案,输出一行表示方便值。
输入输出样例
10 10 10 0 0 7 2 1 4 7 7 7 9 1 2 270 2 3 217 1 4 326 2 5 361 4 6 116 3 7 38 1 8 800 6 9 210 7 10 278 8 9 8 2 8 0 9 3 1 8 0 8 4 2 7 9 7 3 4 7 0 2 2 7 3 2 1 2 3 4
1603 957 7161 9466 3232 5223 1879 1669 1282 0
说明
满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9
两点间距离:
$$dist_{u, v} = dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)}$$
于是推得答案:
$$ans = \sum_{v \in S} (dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)})$$
v为满足条件的点
于是化为
$$= |S|*dist_{root, u}+\sum_{v \in S}dist_{root, v}-2*\sum_{v \in S}dist_{root, lca(u, v)}$$
注意到 $|S|*dist_{root, u}$ 可以直接求;我们按每个点的点权排序,显然 $\sum_{v \in S}dist_{root, v}$ 是可以用前缀和预处理出来的。
现在需要解决的问题就是如何求 $\sum_{v \in S}dist_{root, lca(u, v)}$ 。
求$dist_{root, lca(u, v)}$ 可以这样,先标记root到u的路径,查询v时再求出root到v的的路径和
如果是树上路径的修改和求和,可以用树剖
把点权排序后用主席树,每一棵数Ti表示年龄前1~i个点的线段树
但是新的线段树,树剖修改的区间不止一个,不可能每一个都新建
定义last为上一颗树为止的大小,如果当前节点小于last才新建,如果大于last则说明当前点是属于
这棵树的
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 struct ZYYS 9 { 10 int x,id; 11 bool operator <(const ZYYS &b) const 12 { 13 return x<b.x; 14 } 15 }w[150001]; 16 struct Node 17 { 18 int next,to; 19 lol dis; 20 }edge[300001]; 21 int num,head[150001],size[150001],son[150001],fa[150001],top[150001],dfn[150001],n; 22 int ch[15000001][2],INF=2e9,q,root[150001],pre[150001]; 23 lol sonc[150001],A; 24 lol sum[15000001],lazy[15000001],sumdist[150001],ans; 25 lol val[150001],cost[150001]; 26 int cnt,pos; 27 void add(int u,int v,lol d) 28 { 29 num++; 30 edge[num].next=head[u]; 31 head[u]=num; 32 edge[num].to=v; 33 edge[num].dis=d; 34 } 35 void dfs1(int x,int pa) 36 {int i; 37 size[x]=1; 38 son[x]=0; 39 for (i=head[x];i;i=edge[i].next) 40 { 41 int v=edge[i].to; 42 if (v!=pa) 43 { 44 fa[v]=x; 45 dfs1(v,x); 46 size[x]+=size[v]; 47 if (size[v]>size[son[x]]) son[x]=v,sonc[x]=edge[i].dis; 48 } 49 } 50 } 51 void dfs2(int x,int tp,int pa,lol dis) 52 {int i; 53 top[x]=tp; 54 dfn[x]=++cnt; 55 val[cnt]=dis; 56 if (son[x]) 57 { 58 cost[son[x]]=cost[x]+sonc[x]; 59 dfs2(son[x],tp,x,sonc[x]); 60 } 61 for (i=head[x];i;i=edge[i].next) 62 { 63 int v=edge[i].to; 64 if (v!=pa&&v!=son[x]) 65 { 66 cost[v]=cost[x]+edge[i].dis; 67 dfs2(v,v,x,edge[i].dis); 68 } 69 } 70 } 71 void pushdown(int rt,int l,int r,int last) 72 { 73 int mid=(l+r)/2; 74 if (ch[rt][0]<=last) 75 { 76 int ls=ch[rt][0]; 77 ch[rt][0]=++pos; 78 sum[pos]=sum[ls];ch[pos][0]=ch[ls][0];ch[pos][1]=ch[ls][1]; 79 lazy[pos]=lazy[ls]; 80 } 81 lazy[ch[rt][0]]+=lazy[rt]; 82 sum[ch[rt][0]]+=lazy[rt]*(val[mid]-val[l-1]); 83 if (ch[rt][1]<=last) 84 { 85 int rs=ch[rt][1]; 86 ch[rt][1]=++pos; 87 sum[pos]=sum[rs];ch[pos][0]=ch[rs][0];ch[pos][1]=ch[rs][1]; 88 lazy[pos]=lazy[rs]; 89 } 90 lazy[ch[rt][1]]+=lazy[rt]; 91 sum[ch[rt][1]]+=lazy[rt]*(val[r]-val[mid]); 92 lazy[rt]=0; 93 } 94 void update(int &rt,int l,int r,int L,int R,int last) 95 { 96 if (rt<=last) 97 { 98 int x=rt; 99 rt=++pos; 100 ch[rt][0]=ch[x][0];ch[rt][1]=ch[x][1]; 101 sum[rt]=sum[x];lazy[rt]=lazy[x]; 102 } 103 if (l>=L&&r<=R) 104 { 105 lazy[rt]+=1;sum[rt]+=val[r]-val[l-1]; 106 return; 107 } 108 int mid=(l+r)/2; 109 if (lazy[rt]) pushdown(rt,l,r,last); 110 if (L<=mid) update(ch[rt][0],l,mid,L,R,last); 111 if (R>mid) update(ch[rt][1],mid+1,r,L,R,last); 112 sum[rt]=sum[ch[rt][0]]+sum[ch[rt][1]]; 113 } 114 lol query(int &rt,int l,int r,int L,int R) 115 { 116 //if (!rt) return 0; 117 if (l>=L&&r<=R) 118 { 119 return sum[rt]; 120 } 121 int mid=(l+r)/2; 122 lol s=0; 123 if (lazy[rt]) pushdown(rt,l,r,INF); 124 if (L<=mid) s+=query(ch[rt][0],l,mid,L,R); 125 if (R>mid) s+=query(ch[rt][1],mid+1,r,L,R); 126 //sum[rt]=sum[ch[rt][0]]+sum[ch[rt][1]]; 127 return s; 128 } 129 void change(int &rt,int x,int last) 130 { 131 while (x) 132 { 133 update(rt,1,n,dfn[top[x]],dfn[x],last); 134 x=fa[top[x]]; 135 } 136 } 137 lol ask(int &rt,int x) 138 { 139 lol s=0; 140 while (x) 141 { 142 s+=query(rt,1,n,dfn[top[x]],dfn[x]); 143 x=fa[top[x]]; 144 } 145 return s; 146 } 147 int main() 148 {int i,u,v; 149 lol d,a,b,L,R; 150 cin>>n>>q>>A; 151 for (i=1;i<=n;i++) 152 { 153 scanf("%d",&w[i].x); 154 w[i].id=i; 155 } 156 sort(w+1,w+n+1); 157 for (i=1;i<=n-1;i++) 158 { 159 scanf("%d%d%lld",&u,&v,&d); 160 add(u,v,d);add(v,u,d); 161 } 162 dfs1(1,0);dfs2(1,0,0,0); 163 for (i=1;i<=n;i++) 164 val[i]=val[i-1]+val[i]; 165 for (i=1;i<=n;i++) 166 { 167 root[i]=root[i-1]; 168 change(root[i],w[i].id,pre[i-1]); 169 pre[i]=pos; 170 sumdist[i]=sumdist[i-1]+cost[w[i].id]; 171 } 172 ans=0; 173 for (i=1;i<=q;i++) 174 { 175 scanf("%d%lld%lld",&u,&a,&b); 176 L=min((a+ans)%A,(b+ans)%A);R=max((a+ans)%A,(b+ans)%A); 177 int x1=lower_bound(w+1,w+n+1,(ZYYS){L,0})-w,x2=upper_bound(w+1,w+n+1,(ZYYS){R,0})-w; 178 x2--; 179 lol s1=ask(root[x1-1],u),s2=ask(root[x2],u); 180 ans=(x2-x1+1)*cost[u]+sumdist[x2]-sumdist[x1-1]-2*(s2-s1); 181 printf("%lld\n",ans); 182 } 183 }
其实还有动态点分治做fa♂(转载自Navi_Awson)
查询时,我们发现,在点分树上爬的时候,我们需要统计除了来的子树外的其他子树中所有节点到当前重心的距离,并且加上这一部分通过重心到询问的点的距离。
如何求这一部分通过重心到询问的点的距离?我们可以在处理上个重心的时候预先减去 $size_x*dist(o, fa_x)$ ,再在处理这个重心时加上 $size_x*dist(x, o)$ 。注意 $x$ 是当前处理的重心,即上述两式中 $x$ 的含义不一样。
如何统计除了来的子树外的其他子树中所有节点到当前重心的距离?也用上面的思想,我们可以先减去以上一个重心为根的子树中所有点到当前处理的的重心的距离和,然后再直接加上以当前处理的重心为根的子树中所有的点到重心的距离。
总时间复杂度就是 $O((n+q)log_2 ^2 n)$ 。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 typedef long long lol; 9 struct Node 10 { 11 int next,to; 12 lol dis; 13 }edge[300001]; 14 struct ZYYS 15 { 16 lol x,fax; 17 int age; 18 bool operator <(const ZYYS &b) const 19 { 20 return age<b.age; 21 } 22 }; 23 int num,head[150001],f[150001][21],dep[150001],size[150001],maxsize[150001],minsize,root,age[150001],fa[150001]; 24 int n,Q,A; 25 bool vis[150001]; 26 lol dist[150001],ans; 27 vector<ZYYS>q[150001]; 28 void add(int u,int v,lol w) 29 { 30 num++; 31 edge[num].next=head[u]; 32 head[u]=num; 33 edge[num].to=v; 34 edge[num].dis=w; 35 } 36 void dfs(int x,int father) 37 {int i; 38 f[x][0]=father; 39 dep[x]=dep[father]+1; 40 for (i=head[x];i;i=edge[i].next) 41 { 42 int v=edge[i].to; 43 if (v==father) continue; 44 dist[v]=dist[x]+edge[i].dis; 45 dfs(v,x); 46 } 47 } 48 int lca(int x,int y) 49 {int i; 50 if (dep[x]<dep[y]) swap(x,y); 51 for (i=20;i>=0;i--) 52 if (dep[x]-(1<<i)>=dep[y]) x=f[x][i]; 53 if (x==y) return x; 54 for (i=20;i>=0;i--) 55 if (f[x][i]!=f[y][i]) 56 { 57 x=f[x][i];y=f[y][i]; 58 } 59 return f[x][0]; 60 } 61 lol ask_dist(int x,int y) 62 { 63 int z=lca(x,y); 64 return dist[x]+dist[y]-2*dist[z]; 65 } 66 void get_size(int x,int pa) 67 {int i; 68 size[x]=1; 69 maxsize[x]=0; 70 for (i=head[x];i;i=edge[i].next) 71 { 72 int v=edge[i].to; 73 if (vis[v]||v==pa) continue; 74 get_size(v,x); 75 size[x]+=size[v]; 76 if (size[v]>maxsize[x]) maxsize[x]=size[v]; 77 } 78 } 79 void get_root(int x,int r,int pa) 80 {int i; 81 maxsize[x]=max(maxsize[x],size[r]-size[x]); 82 if (maxsize[x]<minsize) 83 { 84 minsize=maxsize[x]; 85 root=x; 86 } 87 for (i=head[x];i;i=edge[i].next) 88 { 89 int v=edge[i].to; 90 if (vis[v]||v==pa) continue; 91 get_root(v,r,x); 92 } 93 } 94 void get_dist(int x,int rt,int pa,int dis) 95 {int i; 96 q[rt].push_back((ZYYS){dis,ask_dist(x,fa[rt]),age[x]}); 97 for (i=head[x];i;i=edge[i].next) 98 { 99 int v=edge[i].to; 100 if (v==pa||vis[v]) continue; 101 get_dist(v,rt,x,dis+edge[i].dis); 102 } 103 } 104 void solve(int x,int father) 105 {int i; 106 minsize=2e9; 107 get_size(x,0);get_root(x,x,0); 108 int rt=root;vis[rt]=1;fa[rt]=father; 109 q[rt].push_back((ZYYS){0,ask_dist(rt,father),age[rt]}); 110 for (i=head[rt];i;i=edge[i].next) 111 { 112 int v=edge[i].to; 113 if (vis[v]) continue; 114 get_dist(v,rt,rt,edge[i].dis); 115 } 116 q[rt].push_back((ZYYS){0,0,-1}); 117 sort(q[rt].begin(),q[rt].end()); 118 for (i=1;i<q[rt].size();i++) 119 q[rt][i].fax+=q[rt][i-1].fax,q[rt][i].x+=q[rt][i-1].x; 120 for (i=head[rt];i;i=edge[i].next) 121 { 122 int v=edge[i].to; 123 if (vis[v]) continue; 124 solve(v,rt); 125 } 126 } 127 int find(int o,int x) 128 { 129 int l=1,r=q[o].size()-1,as=0; 130 while (l<=r) 131 { 132 int mid=(l+r)/2; 133 if (q[o][mid].age<=x) as=mid,l=mid+1; 134 else r=mid-1; 135 } 136 return as; 137 } 138 void query(int o,int l,int r) 139 {int i; 140 for (i=o;i;i=fa[i]) 141 { 142 int L=find(i,l-1); 143 int R=find(i,r); 144 ans+=(q[i][R].x-q[i][L].x); 145 if (fa[i]) ans-=(q[i][R].fax-q[i][L].fax)+(R-L)*ask_dist(o,fa[i]); 146 ans+=(R-L)*ask_dist(o,i); 147 } 148 } 149 int main() 150 {int i,u,v,j; 151 lol w,a,b,L,R; 152 cin>>n>>Q>>A; 153 for (i=1;i<=n;i++) 154 { 155 scanf("%d",&age[i]); 156 } 157 for (i=1;i<=n-1;i++) 158 { 159 scanf("%d%d%lld",&u,&v,&w); 160 add(u,v,w);add(v,u,w); 161 } 162 dfs(1,0); 163 for (i=1;i<=20;i++) 164 { 165 for (j=1;j<=n;j++) 166 f[j][i]=f[f[j][i-1]][i-1]; 167 } 168 solve(1,0); 169 ans=0; 170 for (i=1;i<=Q;i++) 171 { 172 scanf("%d%lld%lld",&u,&a,&b); 173 L=(a+ans)%A;R=(b+ans)%A; 174 if (L>R) swap(L,R); 175 ans=0; 176 query(u,L,R); 177 printf("%lld\n",ans); 178 } 179 }