题目描述

风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到

人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的
想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面
向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 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

树链剖分+可持久化线段树

这道题和BZOJ3626比较相似.首先考虑没有年龄限制,就是求一些点和一个点x的距离和.答案就是Σdep[i]+dep[x]-2*dep[lca],dep[i]可以处理前缀和,再求出点数乘上dep[x]就就得到了前半部分答案,最后lca深度和怎么求?如果单独求两点x,y的lca深度可以将x到根路径上所有边权值+1(每条边权值相同情况下)然后再求y到根路径上边权和就好了。那么一些点和x的lca深度和也可以用同样的求法,按树剖序建线段树,边权下传到点上,每次跳重链修改和查询。因为这道题边权不同,所以每次给边权+1表示这条边对答案贡献次数+1,维护一个永久化的标记(就是不下传的标记)表示区间要对答案贡献几次,再每个点维护一个贡献和表示这个点代表的区间对答案的总贡献(即这个点在线段树中子树中所有点的永久化标记*区间权值和之和)就行了。那么现在有了年龄限制显然一棵线段树是不行的,因此要把年龄离散化后从小到大每种年龄建一棵可持久化线段树,求年龄区间只要把对应可持久化线段树相减剩下的就只是对应年龄区间的点到根的标记。注意要开longlong。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll ans;
int n,m;
int tot;
int cnt;
int num;
int A,k;
int l,r;
int x,y,z;
int p[150010];
int h[150010];
int f[150010];
int g[150010];
int q[150010];
int v[150010];
int to[300010];
ll s[20000010];
ll sum[150010];
int dep[150010];
int t[20000010];
int son[150010];
int top[150010];
int val[300010];
int head[150010];
int size[150010];
int next[300010];
int root[150010];
int ls[20000010];
int rs[20000010];
ll total[150010];
struct node
{
int x;
int id;
}a[150010];
bool cmp(node a,node b)
{
return a.x<b.x;
}
void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=z;
}
void dfs(int x)
{
size[x]=1;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x])
{
dep[to[i]]=dep[x]+val[i];
f[to[i]]=x;
v[to[i]]=val[i];
dfs(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]])
{
son[x]=to[i];
}
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
p[x]=++num;
q[num]=x;
if(son[x])
{
dfs2(son[x],tp);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x]&&to[i]!=son[x])
{
dfs2(to[i],to[i]);
}
}
}
void pushup(int rt,int l,int r)
{
s[rt]=s[ls[rt]]+s[rs[rt]]+t[rt]*(sum[r]-sum[l-1]);
}
void change(int &rt,int pre,int l,int r,int L,int R)
{
rt=++cnt;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
s[rt]=s[pre];
t[rt]=t[pre];
if(L<=l&&r<=R)
{
t[rt]++;
s[rt]+=sum[r]-sum[l-1];
return ;
}
int mid=(l+r)>>1;
if(L<=mid)
{
change(ls[rt],ls[pre],l,mid,L,R);
}
if(R>mid)
{
change(rs[rt],rs[pre],mid+1,r,L,R);
}
pushup(rt,l,r);
}
ll query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return s[rt];
}
ll res=1ll*t[rt]*(sum[min(r,R)]-sum[max(l,L)-1]);
int mid=(l+r)>>1;
if(L<=mid)
{
res+=query(ls[rt],l,mid,L,R);
}
if(R>mid)
{
res+=query(rs[rt],mid+1,r,L,R);
}
return res;
}
void updata(int x,int rt)
{
while(top[x]!=1)
{
change(root[rt],root[rt],1,n,p[top[x]],p[x]);
x=f[top[x]];
}
change(root[rt],root[rt],1,n,1,p[x]);
}
ll find(int x,int l,int r)
{
ll res=0;
while(top[x]!=1)
{
res+=query(root[r],1,n,p[top[x]],p[x])-query(root[l],1,n,p[top[x]],p[x]);
x=f[top[x]];
}
res+=query(root[r],1,n,1,p[x])-query(root[l],1,n,1,p[x]);
return res;
}
int main()
{
scanf("%d%d%d",&n,&m,&A);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
h[i]=a[i].x;
a[i].id=i;
}
sort(h+1,h+n+1);
k=unique(h+1,h+1+n)-h-1;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1);
dfs2(1,1);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+v[q[i]];
}
for(int i=1;i<=n;i++)
{
int fx=lower_bound(h+1,h+1+k,a[i].x)-h;
g[fx]=i;
if(fx==(lower_bound(h+1,h+1+k,a[i-1].x)-h))
{
total[fx]=total[fx]+1ll*dep[a[i].id];
}
else
{
total[fx]=total[fx-1]+1ll*dep[a[i].id];
root[fx]=root[fx-1];
}
updata(a[i].id,fx);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&z,&x,&y);
l=min((x+ans)%A,(y+ans)%A);
r=max((x+ans)%A,(y+ans)%A);
l=upper_bound(h+1,h+1+k,l-1)-h-1;
r=upper_bound(h+1,h+1+k,r)-h-1;
ans=total[r]-total[l]+1ll*(g[r]-g[l])*dep[z]-2*find(z,l,r);
printf("%lld\n",ans);
}
}

动态点分治+vector

这道题用动态点分治做思路就很简单了,考虑对于单次询问如何用点分治解决。假设查询点为x,对于分治联通块中包含x的分治中心,统计联通块内点权在[L,R]之间的点到分治中心的距离和及点数,这些点到x的距离就是他们先到分治中心的距离+分治中心到x的距离。但与x位于分治中心同一子树中的点不能这样算,所以要容斥减掉这一部分的答案,也就是还要统计与x位于同一子树的这个联通块中所有点到分治中心的距离及点数。那么转换到点分树上就需要在每个点维护这个点在点分树上的子树中各个点权的点到这个点的距离和及点数与这个点在点分树上的子树中各个点权的点到这个点的父节点的距离和及点数。查询时同样往根爬容斥一下即可。对于维护这两种信息大家可能会想用线段树,但线段树的内存这道题显然开不下,不过可以发现所有询问都是在添加信息完成之后再处理,且对信息没有修改,所以可以每个点开两个vector存信息然后再排序。查询时直接在vector上二分查找即可。注意在vector两端开哨兵节点防止越界。

#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define pr pair<int,ll>
using namespace std;
inline char _read()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
int x=0,f=1;char ch=_read();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}
return x*f;
}
ll ans;
int l,r;
int a,b;
int tot;
int dfn;
int num;
int cnt;
int rot;
int n,m,A;
int x,y,z;
int f[150010];
int s[150010];
int h[150010];
int v[150010];
int to[300010];
int lg[300010];
int mx[150010];
ll dep[150010];
int vis[150010];
int val[300010];
int head[150010];
int next[300010];
int size[150010];
int g[19][300010];
vector<ll>sum[150010];
vector<pr>root[150010];
vector<ll>fsum[150010];
vector<int>lev[150010];
vector<pr>froot[150010];
vector<int>flev[150010];
inline void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=z;
}
inline void dfs(int x,int fa)
{
g[0][++dfn]=dep[x];
s[x]=dfn;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa)
{
dep[to[i]]=dep[x]+1ll*val[i];
dfs(to[i],x);
g[0][++dfn]=dep[x];
}
}
}
inline void getroot(int x,int fa)
{
size[x]=1;
mx[x]=0;
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]]&&to[i]!=fa)
{
getroot(to[i],x);
size[x]+=size[to[i]];
mx[x]=max(mx[x],size[to[i]]);
}
}
mx[x]=max(mx[x],num-size[x]);
if(mx[x]<mx[rot])
{
rot=x;
}
}
inline void partation(int x)
{
vis[x]=1;
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]])
{
num=size[to[i]];
rot=0;
getroot(to[i],0);
f[rot]=x;
partation(rot);
}
}
}
inline int lca(int x,int y)
{
x=s[x];
y=s[y];
if(x>y)
{
swap(x,y);
}
int len=lg[y-x+1];
return min(g[len][x],g[len][y-(1<<len)+1]);
}
inline ll dis(int x,int y)
{
return dep[x]+dep[y]-2ll*lca(x,y);
}
inline void insert(int x,int val)
{
for(int i=x;i;i=f[i])
{
root[i].push_back(make_pair(val,dis(x,i)));
if(f[i])
{
froot[i].push_back(make_pair(val,dis(x,f[i])));
}
}
}
inline ll query(int x,int L,int R)
{
ll res=0;
int l,r;
for(int i=x;i;i=f[i])
{
l=upper_bound(lev[i].begin(),lev[i].end(),L-1)-lev[i].begin()-1;
r=upper_bound(lev[i].begin(),lev[i].end(),R)-lev[i].begin()-1;
res+=sum[i][r]-sum[i][l];
if(f[i])
{
l=upper_bound(flev[i].begin(),flev[i].end(),L-1)-flev[i].begin()-1;
r=upper_bound(flev[i].begin(),flev[i].end(),R)-flev[i].begin()-1;
res-=fsum[i][r]-fsum[i][l];
}
}
for(int i=x;f[i];i=f[i])
{
r=(upper_bound(lev[f[i]].begin(),lev[f[i]].end(),R)-lev[f[i]].begin())-(upper_bound(lev[f[i]].begin(),lev[f[i]].end(),L-1)-lev[f[i]].begin());
l=(upper_bound(flev[i].begin(),flev[i].end(),R)-flev[i].begin())-(upper_bound(flev[i].begin(),flev[i].end(),L-1)-flev[i].begin());
res+=dis(x,f[i])*(r-l);
}
return res;
}
int main()
{
n=read();m=read();A=read();
for(int i=1;i<=n;i++)
{
v[i]=read();
h[i]=v[i];
}
sort(h+1,h+1+n);
for(int i=1;i<=n;i++)
{
v[i]=lower_bound(h+1,h+1+n,v[i])-h;
}
for(int i=1;i<n;i++)
{
x=read();y=read();z=read();
add(x,y,z);
add(y,x,z);
}
dfs(1,0);
mx[0]=1<<30;
num=n;
for(int i=2;i<=dfn;i++)
{
lg[i]=lg[i>>1]+1;
}
for(int j=1;(1<<j)<=dfn;j++)
{
for(int i=1;i+(1<<j)-1<=dfn;i++)
{
g[j][i]=min(g[j-1][i],g[j-1][i+(1<<(j-1))]);
}
}
getroot(1,0);
partation(rot);
for(int i=1;i<=n;i++)
{
insert(i,v[i]);
}
for(int i=1;i<=n;i++)
{
sort(root[i].begin(),root[i].end());
int len=root[i].size();
ll res=0;
lev[i].push_back(0);
sum[i].push_back(0);
for(int j=0;j<len;j++)
{
res+=root[i][j].second;
lev[i].push_back(root[i][j].first);
sum[i].push_back(res);
}
lev[i].push_back(n+1);
sum[i].push_back(1<<30);
sort(froot[i].begin(),froot[i].end());
len=froot[i].size();
res=0;
flev[i].push_back(0);
fsum[i].push_back(0);
for(int j=0;j<len;j++)
{
res+=froot[i][j].second;
flev[i].push_back(froot[i][j].first);
fsum[i].push_back(res);
}
flev[i].push_back(n+1);
fsum[i].push_back(1<<30);
}
while(m--)
{
x=read();a=read();b=read();
l=min((1ll*a+ans)%A,(1ll*b+ans)%A);
r=max((1ll*a+ans)%A,(1ll*b+ans)%A);
l=lower_bound(h+1,h+1+n,l)-h;
r=upper_bound(h+1,h+1+n,r)-h-1;
printf("%lld\n",ans=query(x,l,r));
}
}

BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector的更多相关文章

  1. [GDOI2016] 疯狂动物园 [树链剖分+可持久化线段树]

    题面 太长了,而且解释的不清楚,我来给个简化版的题意: 给定一棵$n$个点的数,每个点有点权,你需要实现以下$m$个操作 操作1,把$x$到$y$的路径上的所有点的权值都加上$delta$,并且更新一 ...

  2. 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

    [BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...

  3. 【BZOJ4704】旅行 树链剖分+可持久化线段树

    [BZOJ4704]旅行 Description 在Berland,有n个城堡.每个城堡恰好属于一个领主.不同的城堡属于不同的领主.在所有领主中有一个是国王,其他的每个领主都直接隶属于另一位领主,并且 ...

  4. BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  5. BZOJ 2243:染色(树链剖分+区间合并线段树)

    [SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认 ...

  6. HDU 5029 Relief grain 树链剖分打标记 线段树区间最大值

    Relief grain Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...

  7. 【Codeforces】【网络流】【树链剖分】【线段树】ALT (CodeForces - 786E)

    题意 现在有m个人,每一个人都特别喜欢狗.另外还有一棵n个节点的树. 现在每个人都想要从树上的某个节点走到另外一个节点,且满足要么这个人自带一条狗m,要么他经过的所有边h上都有一条狗. 2<=n ...

  8. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...

  9. HYSBZ 4034 【树链剖分】+【线段树 】

    <题目链接> 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权值.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...

随机推荐

  1. 微信小程序--WXS---JS 代码插入

    主页要使用的JS代码放在index.js里面 例 Page({ data: { array: [1, 2, 3, 4, 5, 1, 2, 3, 4] } }) 只复制以下这段 data: { arra ...

  2. tensorflow:验证码的识别(上)

    验证码的识别 主要分成四个部分:验证码的生成.将生成的图片制作成tfrecord文件.训练识别模型.测试模型 使用pyCharm作为编译器.本文先介绍前两个部分 验证码的识别有两种方法: 验证码识别方 ...

  3. @ResponseBody 和 @RequestBody 的作用

    先提一嘴 @RequestMapping(“url”),这里的 url写的是请求路径的一部分,一般作用在 Controller的方法上,作为请求的映射地址. 代码: @RequestMapping(v ...

  4. UEditor上传自定义文件夹

    需求:使用UEditor上传时需要知道具体到哪个章节得图片,所以得根据Session中得文件重新定义 修改Handler类: public HttpSessionState Session {get; ...

  5. 【第一部分】09Leetcode刷题

    一.位1的个数 题目:191. Number of 1 Bits C++ Soution 1: class Solution { public: int hammingWeight(uint32_t ...

  6. 关于The specified Android SDK Build Tools version (26.0.2) is ignored, as it is below the minimum...

    今天将项目迁移到另一台笔记本,进行build出现以下问题,导致build失败 The specified Android SDK Build Tools version (26.0.2) is ign ...

  7. L1与L2正则(转)

    概念: L0范数表示向量中非零元素的个数:NP问题,但可以用L1近似代替. L1范数表示向量中每个元素绝对值的和: L1范数的解通常是稀疏性的,倾向于选择:1. 数目较少的一些非常大的值  2. 数目 ...

  8. YOLO V2 代码分析

    先介绍YOLO[转]: 第一个颠覆ross的RCNN系列,提出region-free,把检测任务直接转换为回归来做,第一次做到精度可以,且实时性很好. 1. 直接将原图划分为SxS个grid cell ...

  9. HTTP max-age与Expires的分别

    主要重点在于我们要明白一个相对(Expires)一个绝对(max-age). 分别 max-agemax-age是HTTP/1.1中,他是指我们的web中的文件被用户访问(请求)后的存活时间,是个相对 ...

  10. alpha冲刺4/10

    目录 摘要 团队部分 个人部分 摘要 队名:小白吃 组长博客:hjj 作业博客:冲刺4 团队部分 后敬甲 过去两天完成了哪些任务 文字描述 主页部分图标的替换 -拍照按钮的设计和测试 GitHub代码 ...