BZOJ 3991: [SDOI2015]寻宝游戏

本文详细介绍了如何使用LCA算法和虚树概念解决特定的路径查询问题。通过选择一个根节点,计算每个节点的深度和到根的距离,利用这些信息在虚树中找到最短路径。文章深入解析了如何通过维护点集来高效地更新答案,并提供了完整的代码实现。

随便选一个点当做根,跑每个点的深度(为了求LCA)d [ u ] ,和到根节点的距离(为了更新答案) l [ u ]

我们发现,由关键点和他们的LCA构成的虚树(其实就是忽略其他节点),由于还要回到原点,所以相当于是树的所有边权的2倍

怎么求?对于每一次标记,将所有的标记了的点按时间戳排序,那么答案就是一、二两点之间的距离,二、三点之间距离。。。。最后一个点和第一个点之间的距离的总和;

而两点之间的距离等于 l [ u ] + l [ v ] - 2 * l [ lca ( u , v ) ]

维护这些点时可以用set

#include<cstdio>
#include<iostream>
#include<set>
#include<cmath>
#define getchar() *S++
#define ll long long 
#define R register int
const int N=100010,Inf=0x3f3f3f3f;
char RR[100000005],*S=RR;
using namespace std;
inline int g() {
    R ret=0; register char ch; while(!isdigit(ch=getchar())); 
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret;
}
int n,m,num,cnt;
int vr[N<<1],nxt[N<<1],w[N<<1],fir[N],dfn[N],rw[N],d[N],f[N][18];
set<int>s;
set<int>::iterator it;
ll ans,l[N];
bool vis[N];
inline void add(int u,int v,int ww) {vr[++cnt]=v,w[cnt]=ww,nxt[cnt]=fir[u],fir[u]=cnt;}
void dfs(int u) { dfn[u]=++num,rw[num]=u;
    for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(d[v]) continue; d[v]=d[u]+1; l[v]=l[u]+w[i]; f[v][0]=u; R p=u;
        for(R j=0;f[p][j];++j) f[v][j+1]=f[p][j],p=f[p][j];
        dfs(v);
    }
}
inline int lca(int u,int v) {
    if(d[u]<d[v]) swap(u,v); R lim=log2(d[u])+1;
    for(R j=lim;j>=0;--j) if(d[f[u][j]]>=d[v]) u=f[u][j];
    if(u==v) return u;
    for(R j=lim;j>=0;--j) if(f[u][j]!=f[v][j]) u=f[u][j],v=f[v][j];
    return f[u][0];
}
inline ll dis(int u,int v) {return l[u]+l[v]-2*l[lca(u,v)];}
signed main() {
    fread(RR,sizeof(RR),1,stdin);
    n=g(),m=g();
    for(R i=1,u,v,w;i<n;++i) u=g(),v=g(),w=g(),add(u,v,w),add(v,u,w);
    d[1]=1;dfs(1); 
    s.insert(-Inf),s.insert(Inf);
    for(R i=1,x;i<=m;++i) { x=g(); register long long flg; 
        if(vis[x]) s.erase(dfn[x]),flg=-1; else s.insert(dfn[x]),flg=1; vis[x]^=1;
        it=s.upper_bound(dfn[x]); R r=*it,l=*(--it); if(l>=dfn[x]) l=*(--it);
        //cout<<l<<" "<<dfn[x]<<" "<<r<<endl;
        if(l!=-Inf) ans+=flg*dis(rw[l],x); if(r!=Inf) ans+=flg*dis(x,rw[r]);
        if(l!=-Inf&&r!=Inf) ans-=flg*dis(rw[l],rw[r]); 
        register long long tmp=(s.size()>3)?dis(rw[*++s.begin()],rw[*--(--s.end())]):0;
        printf("%lld\n",ans+tmp);
    }
}

2019.04.18

转载于:https://www.cnblogs.com/Jackpei/p/10732622.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值