[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)。

输出格式:

对于每个方案,输出一行表示方便值。

输入输出样例

输入样例#1: 复制
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
输出样例#1: 复制
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
",ans);
182     }
183 }
View Code主席树

其实还有动态点分治做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
",ans);
178      }
179 }
View Code动态点分
原文地址:https://www.cnblogs.com/Y-E-T-I/p/8182129.html