您的位置:首页 > 其它

BZOJ 4012: [HNOI2015]开店

2016-04-13 19:51 405 查看
这叫啥?可持久化树链剖分?

首先忽略掉那个年龄的限制

那就是求所有点到某点的路径之和

这个画个图会发现是 Σdepth[i]+n*depth[u]-2*Σdepth(lca(u,i))

前两个可以O(1)求,最后那个可以用树剖求。

方法就是每个点到根的路径测度+1,这个可以用线段树更新,然后u到根的路径上的测度与长度之积的和就是答案了。

但是出现了那个可恶的限制

不过嘛,我们有传说中的chairman algorithm

于是我们把它变成两个前缀相减,再用可持久化线段树求前缀和

空间O(n(logn)^2),时间O(n(logn)^2)

(由于这题空间不是很好说,于是我把pushdown操作干掉了,每次往下找的时候把标记加起来,然后就上首页了,感觉跑得飞快啊)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=13000000;
const int N=150000+5;
typedef long long ll;
struct Node{
int lc,rc,cov,ti;
ll sum;
}tr[M];
int tot;
int dep
,top
,fa
,siz
,son
,pos
,rank
,cost
,depth
,sz;
struct Edge{int to,next,v;}e[N<<1];
int head
,cnt;
ll len(int l,int r){
return depth[rank[r]]-depth[fa[rank[l]]];
}
void ins(int u,int v,int w){
e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;
}
void dfs(int u){
siz[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa[u])continue;
fa[v]=u;dep[v]=dep[u]+1;cost[v]=e[i].v;depth[v]=depth[u]+cost[v];
dfs(v);
siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs(int u,int tp){
top[u]=tp;pos[u]=++sz;rank[sz]=u;
if(son[u])dfs(son[u],tp);
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v!=son[u]&&v!=fa[u])dfs(v,v);
}
}
ll update(int &o,int l,int r,int a,int b,int ti){
if(tr[o].ti!=ti)tr[++tot]=tr[o],o=tot,tr[tot].ti=ti;
if(l==a&&r==b){tr[o].cov++;tr[o].sum+=len(l,r);return len(l,r);}
else{
int mid=l+r>>1;
ll add=0;
if(b<=mid)add+=update(tr[o].lc,l,mid,a,b,ti);
else if(mid<a)add+=update(tr[o].rc,mid+1,r,a,b,ti);
else add+=update(tr[o].lc,l,mid,a,mid,ti)+update(tr[o].rc,mid+1,r,mid+1,b,ti);
tr[o].sum+=add;
return add;
}
}
ll query(int o,int l,int r,int a,int b,int cov){
if(l==a&&b==r)return tr[o].sum+len(l,r)*cov;
else{
int mid=l+r>>1;
cov+=tr[o].cov;
if(b<=mid)return query(tr[o].lc,l,mid,a,b,cov);
else if(mid<a)return query(tr[o].rc,mid+1,r,a,b,cov);
else return	query(tr[o].lc,l,mid,a,mid,cov)+query(tr[o].rc,mid+1,r,mid+1,b,cov);
}
}
int root
;
int n;
void update(int u,int ti){
while(u){
update(root[ti],1,n,pos[top[u]],pos[u],ti);
u=fa[top[u]];
}
}
ll query(int u,int ti){
ll ans=0;
while(u){
ans+=query(root[ti],1,n,pos[top[u]],pos[u],0);
u=fa[top[u]];
}
return ans;
}
int hash
,id
,rk
;
ll sum
;
bool cmp(int i,int j){
return hash[i]<hash[j];
}
int findl(int l,int r,int x){
while(l<=r){
int mid=l+r>>1;
if(hash[id[mid]]<x)l=mid+1;
else r=mid-1;
}
return l-1;
}
int findr(int l,int r,int x){
while(l<=r){
int mid=l+r>>1;
if(hash[id[mid]]<=x)l=mid+1;
else r=mid-1;
}
return l-1;
}
ll ask(int u,int ti){
return sum[ti]+(ll)ti*depth[u]-2*query(u,ti);
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int q,A;scanf("%d%d%d",&n,&q,&A);
for(int i=1;i<=n;i++){
scanf("%d",&hash[i]);
id[i]=i;
}
sort(id+1,id+1+n,cmp);
for(int i=1;i<n;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
ins(u,v,w);ins(v,u,w);
}
dfs(1);dfs(1,1);
for(int i=1;i<=n;i++){
root[i]=root[i-1];
update(id[i],i);
rk[id[i]]=i;
sum[i]=sum[i-1]+depth[id[i]];
}
ll ans=0;
while(q--){
int u;ll a,b;scanf("%d%lld%lld",&u,&a,&b);
int l=(a+ans)%A,r=(b+ans)%A;
if(l>r)swap(l,r);
l=findl(1,n,l);r=findr(1,n,r);
ans=ask(u,r)-ask(u,l);
printf("%lld\n",ans);
}
return 0;
}
这题好像能用动态点分治做哎,不过我比较懒(明明动态点分治更好写好吗)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: