【SDOI2015】寻宝游戏

本文介绍了一种使用动态虚树解决路径更新问题的方法。通过维护虚树中的路径总长,并利用DFS序和set进行节点增删操作,可以高效地处理路径长度的变化。文章提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SDOI2015-1

样例输入

4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1

样例输出

0
100
220
220
280

提示

SDOI2015-2

题解

本题不断增删关键点,而且有一个显而易见的结论:不管从哪个点出发,每条路径都会走两遍。我们可以维护一棵动态虚树,记录虚树中的路径总长。具体实现需要开一个DFS序的set,增删点类比建虚树过程即可。

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll a,b,c,n,m,ans,cnt,Last[maxn];
ll vt,lca,id[maxn],dep[maxn],dfn[maxn],dis[maxn],fa[maxn][20];
set<ll> cache;
struct node
{
    ll End,Next,Len;
}edge[2*maxn];
void save(ll x,ll y,ll z)
{
    edge[++cnt].End=y,edge[cnt].Len=z;
    edge[cnt].Next=Last[x],Last[x]=cnt;
}
void DFS(ll x)
{
    dep[x]=dep[fa[x][0]]+1;
    dfn[x]=++vt,id[vt]=x;
    ll s=ceil(log2(dep[x]));
    for(ll i=1;i<=s;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(ll i=Last[x];i;i=edge[i].Next)
    {
        ll y=edge[i].End;
        if(y==fa[x][0]) continue;
        fa[y][0]=x,dis[y]=dis[x]+edge[i].Len;
        DFS(y);
    }
}
ll getlca(ll x,ll y)
{
    if(dep[x]<dep[y]) swap(x,y);
    ll k=dep[x]-dep[y],s=ceil(log2(n));
    for(ll i=0;i<=s;i++)
        if(k&(1<<i)) x=fa[x][i];
    if(x==y) return x;
    k=ceil(log2(dep[x]));
    for(ll i=k;i>=0;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
ll pre(ll x)
{
    set<ll>::iterator y=cache.find(dfn[x]);
    if(y==cache.begin()) return 0;
    return id[*(--y)];
}
ll las(ll x)
{
    set<ll>::iterator y=cache.find(dfn[x]);
    y++;
    if(y==cache.end()) return 0;
    return id[*y];
}
void clr(ll x)
{
    ll l=pre(x),r=las(x);
    if(l) lca=getlca(x,l),ans-=dis[x]+dis[l]-2*dis[lca];
    if(r) lca=getlca(x,r),ans-=dis[x]+dis[r]-2*dis[lca];
    if(l&&r) lca=getlca(r,l),ans+=dis[r]+dis[l]-2*dis[lca];
    cache.erase(dfn[x]);
}
void ins(ll x)
{
    cache.insert(dfn[x]);
    ll l=pre(x),r=las(x);
    if(l) lca=getlca(x,l),ans+=dis[x]+dis[l]-2*dis[lca];
    if(r) lca=getlca(x,r),ans+=dis[x]+dis[r]-2*dis[lca];
    if(l&&r) lca=getlca(r,l),ans-=dis[r]+dis[l]-2*dis[lca];
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<n;i++)
    {
        scanf("%lld%lld%lld",&a,&b,&c);
        save(a,b,c),save(b,a,c);
    }
    DFS(1);
    while(m--)
    {
        scanf("%lld",&a);
        if(!cache.size()) cache.insert(dfn[a]),puts("0");
        else
        {
            if(cache.find(dfn[a])!=cache.end()) clr(a);
            else ins(a);
            lca=getlca(id[*cache.begin()],id[*(--cache.end())]);
            printf("%lld\n",ans+dis[id[*cache.begin()]]+dis[id[*(--cache.end())]]-2*dis[lca]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值