bzoj3991: [SDOI2015]寻宝游戏

思路:将关键点按dfs序排序,然后显然就是dfs序中相邻的两个点的距离之和加上最后一个点到第一点的距离(YY一下很显然),动态维护就用splay就好了(我是一个不会用set的蒟蒻。。。。。)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 100005
 
int n,m,tot,deg,l,cnt;
int now[maxn],pre[2*maxn],son[2*maxn],val[2*maxn],dep[maxn],dfn[maxn];
int f[maxn][21];
long long ans,dis[maxn];
bool bo[maxn];
 
inline int read(){
    int x=0;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar());
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}
 
void add(int a,int b,int c){
    son[++tot]=b;
    pre[tot]=now[a];
    now[a]=tot;
    val[tot]=c;
}
 
void link(int a,int b,int c){
    add(a,b,c),add(b,a,c);
}
 
void dfs(int x,int fa){
    dep[x]=dep[fa]+1,dfn[x]=++deg;
    for (int i=1;i<=l;i++) f[x][i]=f[f[x][i-1]][i-1];
    for (int p=now[x];p;p=pre[p])
        if (son[p]!=fa) f[son[p]][0]=x,dis[son[p]]=dis[x]+val[p],dfs(son[p],x);
}
 
int lca(int a,int b){
    if (dep[a]<dep[b]) swap(a,b);int x=dep[a]-dep[b],t=0;
    for (;x;x>>=1,t++) if (x&1) a=f[a][t];
    if (a==b) return a;t=l;
    for (;f[a][0]!=f[b][0];){
        for (;f[a][t]==f[b][t];t--);
        a=f[a][t],b=f[b][t];
    }
    return f[a][0];
}
 
struct splay_tree{
    int ch[maxn][2],fa[maxn],note[maxn],val[maxn],pos[maxn],root,tot;
    void rotate(int x){
        int y=fa[x],z=fa[y],bo=note[x],bo1=note[y];
        ch[y][bo]=ch[x][bo^1],fa[ch[x][bo^1]]=y;
        ch[x][bo^1]=y,fa[y]=x;
        fa[x]=z;if (bo1!=2) ch[z][bo1]=x;
        note[x]=bo1,note[y]=bo^1,note[ch[y][bo]]=bo;
    }
    void splay(int x){
        while (note[x]!=2){
            if (note[x]==note[fa[x]]) rotate(fa[x]);
            rotate(x);
        }
        root=x;
    }
    void insert(int x,int v,int p){
        int y;
        while (1){
            y=ch[x][val[x]<v];
            if (!y){
                y=++tot;
                val[y]=v,pos[y]=p;
                if (x) note[y]=val[x]<v;else note[y]=2;
                fa[y]=x,ch[y][0]=ch[y][1]=0;
                if (x) ch[x][note[y]]=y;
                break;
            }
            x=y;
        }
        splay(y);
    }
    int find(int v){
        int x=root;
        while (1){
            if (val[x]==v) return splay(x),x;
            if (val[x]<v) x=ch[x][1];else x=ch[x][0];
        }
    }
    int pre(int x){
        splay(x);x=ch[x][0];
        while (ch[x][1]) x=ch[x][1];
        return x;
    }
    int suc(int x){
        splay(x);x=ch[x][1];
        while (ch[x][0]) x=ch[x][0];
        return x;
    }
    int getmin(){int x=root;while (ch[x][0]) x=ch[x][0];return x;}
    int getmax(){int x=root;while (ch[x][1]) x=ch[x][1];return x;}
    void del(int v){
        int x=find(v);int y=suc(x),z=ch[x][0];
        if (!y){
            if (!z){root=0;return;}
            fa[z]=0,note[z]=2,root=z;return;
        }
        fa[ch[x][1]]=0,note[ch[x][1]]=2,splay(y);
        fa[z]=y,ch[y][0]=z,root=y;
    }
}S;
 
int main(){
    n=read(),m=read();l=log2(n);
    for (int i=1,a,b,c;i<n;i++) a=read(),b=read(),c=read(),link(a,b,c);
    dfs(1,0);
    while (m--){
        int x=read();bo[x]^=1;
        if (bo[x]) S.insert(S.root,dfn[x],x),cnt++;
        int a=S.pre(S.find(dfn[x])),b=S.suc(S.find(dfn[x]));
        if (!a) a=S.getmax();if (!b) b=S.getmin();
        a=S.pos[a],b=S.pos[b];
        if (!bo[x]) S.del(dfn[x]),cnt--;
        if (cnt<=1){puts("0"),ans=0;continue;}
        ans+=(bo[x]?1:-1)*(dis[a]+dis[x]-2*dis[lca(a,x)]);
        ans+=(bo[x]?1:-1)*(dis[x]+dis[b]-2*dis[lca(b,x)]);
        ans+=(bo[x]?-1:1)*(dis[a]+dis[b]-2*dis[lca(a,b)]);
        printf("%lld\n",ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/DUXT/p/5993739.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值