[BZOJ]4012: [HNOI2015]开店 树链剖分+主席树(线段树合并)
2017-11-23 17:01
453 查看
Description
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
题解:
好题!考虑两点间计算距离的公式:dis[x]+dis[y]−2×dis[lca(x,y)],那么对于年龄在[L,R]区间的妖怪,统计一下[1,i]区间的妖怪个数和这些妖怪到根的距离之和的前缀和是很容易的,所以关键是所有的lca到根的距离之和怎么求。我们对年龄从小到大排序、离散化,对于每个妖怪,把它到根的路径上的边都+1,表示这条边被经过了1次,那么对于每次的点u,它与所有点的lca到根的距离之和就是u到根上的边的边权×次数之和,由于规定区间[L,R],所以要可持久化线段树。但是要注意这个主席树是改段的,开空间要大(具体不太会算),然后lazy标记也不能下传,不然会有各种各样的问题,需要标记永久化。我看的是fyc的博客。代码:
#include<bits/stdc++.h> using namespace std; #define LL long long #define pa pair<int,int> const int Maxn=150010; const int inf=2147483647; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x*f; } LL A; int n,Q,a[Maxn]; struct Age{int x,id;}b[Maxn]; bool cmp(Age a,Age b){return a.x<b.x;} struct Edge{int y,d,next;}e[Maxn<<1]; int last[Maxn],len=0; void ins(int x,int y,int d){int t=++len;e[t].y=y;e[t].d=d;e[t].next=last[x];last[x]=t;} int dep[Maxn],tot[Maxn],son[Maxn],ys[Maxn],dfn=0,fa[Maxn],top[Maxn],val[Maxn]; void dfs1(int x,int f) { dep[x]=dep[f]+1;fa[x]=f;tot[x]=1;son[x]=0; for(int i=last[x];i;i=e[i].next) { int y=e[i].y; if(y!=f) { dfs1(y,x); tot[x]+=tot[y]; son[x]=(tot[son[x]]<tot[y])?y:son[x]; } } } LL Sum[Maxn],S1[Maxn],S2[Maxn],dis[Maxn]; void dfs2(int x,int tp,int d,LL Dis) { ys[x]=++dfn;top[x]=tp;val[dfn]=d;S2[a[x]]+=(dis[x]=Dis); if(son[x]) { int o; for(int i=last[x];i;i=e[i].next) if(e[i].y==son[x]){o=e[i].d;break;} dfs2(son[x],tp,o,Dis+o); } for(int i=last[x];i;i=e[i].next) { int y=e[i].y; if(y!=fa[x]&&y!=son[x])dfs2(y,y,e[i].d,Dis+e[i].d); } } int root[Maxn],lc[Maxn*75],rc[Maxn*75],Cnt=0; LL s[Maxn*75],lazy[Maxn*75]; void insert(int &u,int l,int r,int ll,int rr)//[ll,rr]为当前需要修改的区间 { if(!u)u=++Cnt; s[u]+=Sum[rr]-Sum[ll-1]; if(l==ll&&r==rr){lazy[u]++;return;} int mid=l+r>>1; if(rr<=mid)insert(lc[u],l,mid,ll,rr); else if(ll>mid)insert(rc[u],mid+1,r,ll,rr); else insert(lc[u],l,mid,ll,mid),insert(rc[u],mid+1,r,mid+1,rr); } void merge(int &u1,int u2) { if(!u1){u1=u2;return;} if(!u2)return; s[u1]+=s[u2];lazy[u1]+=lazy[u2]; merge(lc[u1],lc[u2]);merge(rc[u1],rc[u2]); } LL query(int rt1,int rt2,int l,int r,int ll,int rr,LL tag) { if(l==ll&&r==rr)return s[rt1]-s[rt2]+tag*(Sum[r]-Sum[l-1]); int mid=l+r>>1; if(rr<=mid)return query(lc[rt1],lc[rt2],l,mid,ll,rr,tag+lazy[rt1]-lazy[rt2]); else if(ll>mid)return query(rc[rt1],rc[rt2],mid+1,r,ll,rr,tag+lazy[rt1]-lazy[rt2]); else return query(lc[rt1],lc[rt2],l,mid,ll,mid,tag+lazy[rt1]-lazy[rt2])+ query(rc[rt1],rc[rt2],mid+1,r,mid+1,rr,tag+lazy[rt1]-lazy[rt2]); } void work(int x,int y,int rt) { int tx=top[x],ty=top[y]; while(tx!=ty) { if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty); insert(rt,1,n,ys[tx],ys[x]); x=fa[tx];tx=top[x]; } if(x==y)return; if(dep[x]<dep[y])swap(x,y); insert(rt,1,n,ys[son[y]],ys[x]); } int Find1(int x)//找后继 可以等于 { int l=1,r=n; while(l<=r) { int mid=l+r>>1; if(b[mid].x>=x)r=mid-1; else l=mid+1; }return a[b[r+1].id]; } int Find2(int x)//找前驱 可以等于 { int l=1,r=n; while(l<=r) { int mid=l+r>>1; if(b[mid].x<=x)l=mid+1; else r=mid-1; }return a[b[l-1].id]; } LL solve(int u,int x,int y) { x=Find1(x);y=Find2(y); if(x>y)return 0; LL re=0; int X=x,Y=y,rt1=root[Y],rt2=root[X-1]; x=1,y=u;int tx=top[x],ty=top[y]; while(tx!=ty) { if(dep[tx]<dep[ty])swap(x,y),swap(tx,ty); re+=query(rt1,rt2,1,n,ys[tx],ys[x],0); x=fa[tx];tx=top[x]; } if(x==y)return (S1[Y]-S1[X-1])*dis[u]+S2[Y]-S2[X-1]-2LL*re; if(dep[x]<dep[y])swap(x,y); return (S1[Y]-S1[X-1])*dis[u]+S2[Y]-S2[X-1]-2LL*(re+query(rt1,rt2,1,n,ys[son[y]],ys[x],0)); } int main() { n=read(),Q=read(),A=read(); for(int i=1;i<=n;i++)b[i].x=read(),b[i].id=i; sort(b+1,b+1+n,cmp); int cnt=0;b[0].x=-1; for(int i=1;i<=n;i++)a[b[i].id]=((b[i].x!=b[i-1].x)?(++cnt):cnt); b[0].id=n+2;b[n+1].id=n+1;a[n+1]=cnt+1;a[n+2]=0; for(int i=1;i<n;i++) { int x=read(),y=read(),d=read(); ins(x,y,d);ins(y,x,d); } dep[0]=0;dfs1(1,0); dfs2(1,1,0,0); for(int i=1;i<=n;i++)S1[a[i]]++; Sum[0]=0;for(int i=1;i<=n;i++)Sum[i]=Sum[i-1]+(LL)(val[i]),S1[i]+=S1[i-1],S2[i]+=S2[i-1]; int now=1,o=1;root[0]=0; while(now<=n) { root[o]=++Cnt; work(1,b[now].id,root[o]); while(now<n&&b[now].x==b[now+1].x)work(1,b[++now].id,root[o]); now++; merge(root[o],root[o-1]); o++; } LL ans=0; while(Q--) { int u=read(),l,r; LL _a=read(),_b=read(); l=min((_a+ans)%A,(_b+ans)%A);r=max((_a+ans)%A,(_b+ans)%A); ans=solve(u,l,r); printf("%lld\n",ans); } }
相关文章推荐
- bzoj 4012: [HNOI2015]开店 (树链剖分+主席树)
- bzoj4012 [HNOI2015]开店(动态点分治+二分+STL/树链剖分+主席树)
- [BZOJ4012][HNOI2015]开店(动态点分治,树链剖分)
- bzoj 4012: [HNOI2015]开店 主席树
- BZOJ4012: [HNOI2015]开店 重链剖分 可持久化线段树
- bzoj 4012 [HNOI2015]开店 【树链剖分】
- bzoj4012 [HNOI2015]开店
- BZOJ 4012 HNOI 2015 开店(shop) 一道简单的点剖题
- 【bzoj4012】[HNOI2015]开店 动态点分治+STL-vector
- 【BZOJ】4012: [HNOI2015]开店
- BZOJ4012 [HNOI2015]开店 【动态点分治 + splay】
- BZOJ4012 [HNOI2015]开店
- bzoj 4012: [HNOI2015]开店
- BZOJ 4012 [HNOI2015]开店
- 【bzoj4012】 HNOI2015—开店
- 【动态树分治】【bzoj 4012】: [HNOI2015]开店
- [BZOJ4012][HNOI2015]开店(动态点分治)
- BZOJ4012 [HNOI2015]开店
- bzoj 4012: [HNOI2015]开店
- BZOJ 4012: [HNOI2015]开店 -- 动态树分治