您的位置:首页 > 其它

[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 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

题解:

好题!考虑两点间计算距离的公式: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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: