【LOJ#6073】距离(主席树)

【LOJ#6073】距离(主席树)

题面

LOJ

题解

两点间的距离是\(dep[x]+dep[y]-2dep[LCA]\)
那么题目要求的东西拆开维护,唯一不好做的就是\(2dep[LCA]\)
而现在要求的是某个单点与一个点集的所有\(LCA\)的深度和。
那么把点集中每一个点到根的路径全部标记一次路径长度,那么只需要求单点到根的权值和就好了。
回到当前题目,既然要求的是一条路径上的东西,那么用主席树维护其到根的点集所影响的权值,最后减一减就好了。
(是不是说得太简单了。。。。)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 200200
inline ll read()
{
    ll x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int type,n,Q,p[MAX];
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int dfn[MAX],low[MAX],tim,top[MAX],size[MAX],hson[MAX],dep[MAX],fa[MAX],V[MAX];
ll sum[MAX],ans,dis[MAX],sdis[MAX];
void dfs1(int u,int ff)
{
    size[u]=1;fa[u]=ff;dep[u]=dep[ff]+1;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;
        dis[v]=dis[u]+e[i].w;V[v]=e[i].w;
        dfs1(v,u);size[u]+=size[v];
        if(size[v]>size[hson[u]])hson[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp;dfn[u]=++tim;
    if(hson[u])dfs2(hson[u],tp);
    for(int i=h[u];i;i=e[i].next)
        if(e[i].v!=fa[u]&&e[i].v!=hson[u])
            dfs2(e[i].v,e[i].v);
}
int LCA(int u,int v)
{
    while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
    return dep[u]<dep[v]?u:v;
}
struct Node{int ls,rs,s;ll v;}t[MAX<<6];
int tot,rt[MAX];
void Modify(int &x,int l,int r,int L,int R)
{
    t[++tot]=t[x];x=tot;
    if(l==L&&r==R){++t[x].s;return;}
    t[x].v+=sum[R]-sum[L-1];int mid=(l+r)>>1;
    if(R<=mid)Modify(t[x].ls,l,mid,L,R);
    else if(L>mid)Modify(t[x].rs,mid+1,r,L,R);
    else Modify(t[x].ls,l,mid,L,mid),Modify(t[x].rs,mid+1,r,mid+1,R);
}
ll Query(int x,int l,int r,int L,int R)
{
    ll ret=(sum[R]-sum[L-1])*t[x].s;
    if(L==l&&R==r)return ret+t[x].v;
    int mid=(l+r)>>1;
    if(R<=mid)return ret+Query(t[x].ls,l,mid,L,R);
    if(L>mid)return ret+Query(t[x].rs,mid+1,r,L,R);
    return ret+Query(t[x].ls,l,mid,L,mid)+Query(t[x].rs,mid+1,r,mid+1,R);
}
void dfs(int u,int ff)
{
    rt[u]=rt[ff];int x=p[u];sdis[u]=sdis[ff]+dis[x];
    while(top[x])Modify(rt[u],1,n,dfn[top[x]],dfn[x]),x=fa[top[x]];
    for(int i=h[u];i;i=e[i].next)
        if(e[i].v!=ff)dfs(e[i].v,u);
}
ll Query(int u,int k)
{
    ll ret=dis[k]*dep[u]+sdis[u];
    while(top[k])ret-=Query(rt[u],1,n,dfn[top[k]],dfn[k])*2,k=fa[top[k]];
    return ret;
}
int main()
{
    type=read();n=read();Q=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read(),w=read();
        Add(u,v,w);Add(v,u,w);
    }
    dfs1(1,0);dfs2(1,1);
    for(int i=1;i<=n;++i)p[i]=read();
    for(int i=1;i<=n;++i)sum[dfn[i]]=V[i];
    for(int i=1;i<=n;++i)sum[i]+=sum[i-1];
    dfs(1,0);
    while(Q--)
    {
        int u=read()^(type*ans),v=read()^(type*ans);
        int lca=LCA(u,v),k=read()^(type*ans);
        printf("%lld\n",ans=Query(u,k)+Query(v,k)-Query(lca,k)-Query(fa[lca],k));
    }
    
}

转载于:https://www.cnblogs.com/cjyyb/p/10202281.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值