[主席树 链剖] LOJ#6073. 「2017 山东一轮集训 Day5」距离

刚开始想了一个很naive的分块加虚树的做法,不管时空复杂度还是代码复杂度都巨大

可以把问题转发成求

ipath(u,root)dis(pi,k)

再推一下式子就得到

ans=ipath(u,root)dis(pi)+depthu×dis(k)2ipath(u,root)dis(lca(i,k))

在树上维护一个主席树,最后一个sigma可以用链剖加线段树维护出来

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N=200010,M=N*300;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void read(ll &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

int type,n,q,cnt,G[N],p[N],top[N],size[N],son[N],fa[N];
struct edge{
  int t,nx,w;
}E[N<<1];

inline void addedge(int x,int y,int z){
  E[++cnt].t=y; E[cnt].nx=G[x]; E[cnt].w=z; G[x]=cnt;
  E[++cnt].t=x; E[cnt].nx=G[y]; E[cnt].w=z; G[y]=cnt;
}

int dpt[N];
ll dis[N],sdis[N];

void dfs1(int x,int f){
  size[x]=1; fa[x]=f; dpt[x]=dpt[f]+1;
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f){
      dis[E[i].t]=dis[x]+E[i].w;
      dfs1(E[i].t,x);
      if(size[E[i].t]>size[son[x]]) son[x]=E[i].t;
      size[x]+=size[E[i].t];
    }
}

int lst[N],bg[N],ed[N],t;

void dfs2(int x,int tp){
  top[x]=tp; lst[++t]=x; bg[x]=t;
  if(son[x]) dfs2(son[x],tp);
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=fa[x] && E[i].t!=son[x]) dfs2(E[i].t,E[i].t);
  ed[x]=t;
}

int tot1,tot2,ls[M],rs[M],rt1[N],rt2[N],sum[N*20],lss[N*20],rss[N*20];
ll tot[M];

void Add(int &g,int x,int l,int r){
  int k=g; g=++tot2; lss[g]=lss[k]; rss[g]=rss[k]; sum[g]=sum[k]+1;
  if(l==r) return ;
  int mid=l+r>>1;
  if(x<=mid) Add(lss[g],x,l,mid); else Add(rss[g],x,mid+1,r);
}

void Add(int &g,int x,int l,int r,ll y){
  int k=g; g=++tot1; ls[g]=ls[k]; rs[g]=rs[k]; tot[g]=tot[k]+y;
  if(l==r) return ;
  int mid=l+r>>1;
  if(x<=mid) Add(ls[g],x,l,mid,y); else Add(rs[g],x,mid+1,r,y);
}

inline void Modify(int &g1,int &g2,int x){
  Add(g2,bg[x],1,n);
  while(x){
    Add(g1,bg[x],1,n,dis[x]); x=fa[top[x]];
  }
}

void pfs(int x){
  sdis[x]=sdis[fa[x]]+dis[p[x]];
  rt1[x]=rt1[fa[x]]; rt2[x]=rt2[fa[x]];
  Modify(rt1[x],rt2[x],p[x]);
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=fa[x]) pfs(E[i].t);
}

ll ans;

void PutAns(ll x){
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

inline int lca(int u,int v){
  while(top[u]!=top[v]){
    if(dpt[top[u]]<dpt[top[v]]) swap(u,v);
    u=fa[top[u]];
  }
  return dpt[u]<dpt[v]?u:v;
}

ll Query1(int g,int l,int r,int L,int R){
  if(l==L && r==R) return tot[g];
  int mid=L+R>>1;
  if(r<=mid) return Query1(ls[g],l,r,L,mid);
  else if(l>mid) return Query1(rs[g],l,r,mid+1,R);
  else return Query1(ls[g],l,mid,L,mid)+Query1(rs[g],mid+1,r,mid+1,R);
}

int Query2(int g,int l,int r,int L,int R){
  if(l==L && r==R) return sum[g];
  int mid=L+R>>1;
  if(r<=mid) return Query2(lss[g],l,r,L,mid);
  else if(l>mid) return Query2(rss[g],l,r,mid+1,R);
  return Query2(lss[g],l,mid,L,mid)+Query2(rss[g],mid+1,r,mid+1,R);
}

inline ll Query(int u,int k){
  ll ret=sdis[u]+dpt[u]*dis[k]-2*Query2(rt2[u],bg[k],ed[k],1,n)*dis[k];
  while(1){
    if(k!=top[k]){
      k=fa[k];
      ret-=2*Query1(rt1[u],bg[top[k]],bg[k],1,n);
    }
    k=top[k];
    if(!fa[k]) break;
    ret-=2*dis[fa[k]]*(Query2(rt2[u],bg[fa[k]],ed[fa[k]],1,n)-Query2(rt2[u],bg[k],ed[k],1,n));
    k=fa[k];
  }
  return ret;
}

inline ll Query(int u,int v,int k){
  int LCA=lca(u,v);
  return Query(u,k)+Query(v,k)-Query(LCA,k)-Query(fa[LCA],k);
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(type); read(n); read(q);
  for(int i=1,x,y,z;i<n;i++)
    read(x),read(y),read(z),addedge(x,y,z);
  for(int i=1;i<=n;i++) read(p[i]);
  dfs1(1,0); dfs2(1,1); pfs(1);
  while(q--){
    ll u,v,k; read(u); read(v); read(k);
    u^=ans*type; v^=ans*type; k^=ans*type;
    PutAns(ans=Query(u,v,k)); putchar('\n');
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值