【BZOJ】3991: [SDOI2015]寻宝游戏

题意

给一个\(n\)个点带边权的树。有\(m\)次操作,每一次操作一个点\(x\),如果\(x\)已经出现,则\(x\)消失。否则\(x\)出现。每一操作后,询问从某个点开始走,直到经过所有出现的点,最后再回到开始的那个点的最短路程。

分析

首先容易知道我们选任意一个在某两点路径上的点作为起点都能得到最优解(包括端点)。我们只需要考虑走的顺序。

题解

由于按照dfs序的走法是最短的,因此我们按dfs序维护一下前前后后的距离和即可。如何证明?好像并不会...

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int n, ihead[N], FF[N], dep[N], cnt, a[N], fa[N][17], m;
ll d[N];
struct E {
    int next, to, w;
}e[N<<1];
struct dat {
    int FF, id;
    ll dis;
    bool operator < (const dat &a) const {
        return FF<a.FF;
    }
};
set<dat> s;
void add(int x, int y, int w) {
    e[++cnt]=(E){ihead[x], y, w}; ihead[x]=cnt;
    e[++cnt]=(E){ihead[y], x, w}; ihead[y]=cnt;
}
void dfs(int x, int f=0) {
    static int fid=0;
    FF[x]=++fid;
    for(int i=1; i<=16; ++i) {
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }
    for(int i=ihead[x]; i; i=e[i].next) {
        int y=e[i].to;
        if(y==f) {
            continue;
        }
        fa[y][0]=x;
        dep[y]=dep[x]+1;
        d[y]=d[x]+e[i].w;
        dfs(y, x);
    }
}
int LCA(int x, int y) {
    if(dep[x]<dep[y]) {
        swap(x, y);
    }
    int d=dep[x]-dep[y];
    for(int i=16; i>=0; --i) if((d>>i)&1) x=fa[x][i];
    if(x==y) return x;
    for(int i=16; i>=0; --i) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
    return fa[x][0];
}
ll getdis(int x, int y) {
    int lca=LCA(x, y);
    return d[x]+d[y]-(d[lca]<<1);
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=1; i<n; ++i) {
        int x, y, w;
        scanf("%d%d%d", &x, &y, &w);
        add(x, y, w);
    }
    dfs((n+1)>>1);
    ll ans=0;
    while(m--) {
        int p;
        scanf("%d", &p);
        dat t;
        if(a[p]) {
            set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end(), itb=s.end();
            itb=it;
            ++itb;
            ans-=it->dis;
            if(it!=s.begin()) {
                itp=it;
                --itp;
            }
            if(itb!=s.end()) {
                ans-=itb->dis;
                if(itp==s.end()) {
                    t=*itb;
                    t.dis=0;
                    s.erase(itb);
                    s.insert(t);
                }
                else {
                    t=*itb;
                    t.dis=getdis(itb->id, itp->id);
                    ans+=t.dis;
                    s.erase(itb);
                    s.insert(t);
                }
            }
            s.erase(it);
            a[p]=0;
        }
        else {
            set<dat>::iterator it=s.lower_bound((dat){FF[p], 0, 0}), itp=s.end();
            if(it!=s.begin()) {
                itp=it;
                --itp;
            }
            if(it!=s.end()) {
                if(itp!=s.end()) {
                    ll dis=getdis(p, itp->id);
                    s.insert((dat){FF[p], p, dis});
                    ans+=dis;
                }
                else {
                    s.insert((dat){FF[p], p, 0});
                }
                t=*it;
                ans-=t.dis;
                t.dis=getdis(p, it->id); 
                ans+=t.dis;
                s.erase(it);
                s.insert(t);
            }
            else {
                if(itp==s.end()) {
                    s.insert((dat){FF[p], p, 0});
                }
                else {
                    ll dis=getdis(p, itp->id);
                    s.insert((dat){FF[p], p, dis});
                    ans+=dis;
                }
            }
            a[p]=1;
        }
        ll temp=0;
        if(s.size()>=2) {
            set<dat>::iterator it=s.end();
            --it;
            temp=getdis(s.begin()->id, it->id);
        }
        printf("%lld\n", ans+temp);
    }
    return 0;
}

转载于:https://www.cnblogs.com/iwtwiioi/p/4986317.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值