Loj #6073.「2017 山东一轮集训 Day5」距离

Loj #6073.「2017 山东一轮集训 Day5」距离

Description

给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\),设$ path(u,v) \(表示\) u$ 到 $v \(的路径,\)dist(u,v) \(表示\) u$ 到\(v\) 的距离,希望你求出
img

Input

第一行一个整数 \(type =0/1\)表示这个测试点的数据类型。
第二行两个整数 \(n,q\)
接下来$ n−1$ 行,每行三个整数 \(ui,vi,ci,\)代表树上有一条连接$ ui,vi$ 的权值为$ci $的边。
接下来一行 \(n\) 个正整数表示给定的排列 p。
接下来 \(q\) 行,每行三个整数 \(u′,v′,k′\),记lastAns 为上一次询问的答案,假如这是第一次则\(lastAns=0\),那么这个询问对应的\(u,v,k\) 满足:
img

有一篇写的很不错的博客

思路还是比较妙啊。

题目的难点在于求一个点与一个点集的\(lca\)深度之和。我们可以将点集中的每个点到根的路径上的标记都\(+1\)。询问点\(k\)到这个点集的\(lca\)深度和的时候我们就可以询问该点到根路径上的所有边权与标记的乘积之和。

由于是询问\((a,b)\)路径上的信息,我们就用主席树维护,询问的时候做差分。对于一个节点\(a\),它的信息由\(fa_a\)继承下来,再加上\(p_a\)的信息就好了。

考试时不会,写的虚树,常数大到自闭。

[HNOI2015]开店 的主席树做法也是基于这个原理的。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 200005

using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m,type;
int p[N];
ll pre[N];
ll ans;
struct road {
    int to,next;
    ll d;
}s[N<<1];
int h[N],cnt;
void add(int i,int j,ll d) {s[++cnt]=(road) {j,h[i],d};h[i]=cnt;}

ll sum_dis[N];
int size[N],son[N];
int fa[N],top[N],dep[N];
ll dis[N];

void dfs(int v) {
    size[v]=1;
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(to==fa[v]) continue ;
        dep[to]=dep[v]+1;
        dis[to]=dis[v]+s[i].d;
        fa[to]=v;
        dfs(to);
        size[v]+=size[to];
        if(size[son[v]]<size[to]) son[v]=to;
    }
}

int dfn[N],id;
void dfs2(int v,int tp) {
    sum_dis[v]=sum_dis[fa[v]]+dis[p[v]];
    dfn[v]=++id;
    pre[dfn[v]]=dis[v]-dis[fa[v]];
    top[v]=tp;
    if(son[v]) dfs2(son[v],tp);
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(to==fa[v]||to==son[v]) continue ;
        dfs2(to,to);
    }
}

int lca(int a,int b) {
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        a=fa[top[a]];
    }
    return dep[a]<dep[b]?a:b;
}

int tot;
int rt[N];
int lx,rx;
struct tree {
    int ls,rs;
    ll tag,sum;
}tr[N*150];

void Modify(int &v,int old,int lx,int rx,int l,int r,ll f) {
    if(lx>r||rx<l) return ;
    v=++tot;
    tr[v]=tr[old];
    if(l<=lx&&rx<=r) {
        tr[v].tag+=f;
        return ;
    }
    int L=max(lx,l),R=min(rx,r);
    tr[v].sum+=f*(pre[R]-pre[L-1]);
    int mid=lx+rx>>1;
    Modify(tr[v].ls,tr[old].ls,lx,mid,l,r,f);
    Modify(tr[v].rs,tr[old].rs,mid+1,rx,l,r,f);
}

void Modify(int v) {
    int a=p[v];
    while(top[a]!=top[1]) {
        Modify(rt[v],rt[v],lx,rx,dfn[top[a]],dfn[a],1);
        a=fa[top[a]];
    }
    Modify(rt[v],rt[v],lx,rx,dfn[1],dfn[a],1);
}

ll query(int v,int lx,int rx,int l,int r) {
    if(!v||lx>r||rx<l) return 0;
    if(l<=lx&&rx<=r) return tr[v].sum+tr[v].tag*(pre[rx]-pre[lx-1]);
    ll ans=tr[v].tag*(pre[min(r,rx)]-pre[max(l,lx)-1]);
    int mid=lx+rx>>1;
    ans+=query(tr[v].ls,lx,mid,l,r)+query(tr[v].rs,mid+1,rx,l,r);
    return ans;
}

ll query(int rt,int a) {
    ll ans=0;
    while(top[a]!=top[1]) {
        ans+=query(rt,lx,rx,dfn[top[a]],dfn[a]);
        a=fa[top[a]];
    }
    ans+=query(rt,lx,rx,dfn[1],dfn[a]);
    return ans;
}

void dfs3(int v) {
    rt[v]=rt[fa[v]];
    Modify(v);
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(to==fa[v]) continue ;
        dfs3(to);
    }
}

int main() {
    type=Get();
    n=Get(),m=Get();
    int a,b,d;
    for(int i=1;i<n;i++) {
        a=Get(),b=Get(),d=Get();
        add(a,b,d),add(b,a,d);
    }
    for(int i=1;i<=n;i++) p[i]=Get();
    lx=1,rx=n;
    dfs(1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) pre[i]+=pre[i-1];
    dfs3(1);
    
    while(m--) {
        ll x=Get()^(ans*type),y=Get()^(ans*type),k=Get()^(ans*type);
        int f=lca(x,y);
        ans=sum_dis[x]+sum_dis[y]-sum_dis[f]-sum_dis[fa[f]];
        ans+=(dep[x]+dep[y]-2*dep[f]+1)*dis[k];
        ans-=2*(query(rt[x],k)+query(rt[y],k)-query(rt[f],k)-query(rt[fa[f]],k));
        cout<<ans<<"\n";
    }
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10503205.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值