码迷,mamicode.com
首页 > 其他好文 > 详细

[SDOI2015]寻宝游戏

时间:2019-12-22 18:04:21      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:lse   vector   find   ==   --   dig   ++   name   print   

Link

Description

 给定一棵n个点的树,树上有若干关键点。每次操作将一个关键点变成非关键点或一个非关键点变为关键点,并询问所有关键点形成的极小联通子树边权和的两倍。

Solution

先摆结论:

 假设树上关键点按照\(dfs\)序排列为{\(a_1,a_2,a_3,\dots,a_t\)}。

 那么所有关键点形成的极小联通子树边权和的两倍会等于:

 \(dis(a_1,a_2)+dis(a_2,a_3)+\dots+dis(a_{t-1},a_t)+dis(a_t,a_1)\)

感性偷税发现很正确。

所以直接用一个set维护关键点按\(dfs\)序排列成的集合,每次加一个点x,假设其两边分别是y,z,那么答案就要加上\(dis(x,y)+dis(x,z)-dis(y,z)\)。删掉一个点则反之。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef set<int>::iterator sit;
inline int read(){//be careful for long long!
    register int x=0,f=1;register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
    return f?x:-x;
}

const int N=1e5+10;
int n,m,dfn[N],idf[N],dfc,siz[N],tp[N],dep[N],fa[N],son[N],vis[N];
ll dis[N];
struct node{int to,val;};
vector<node> E[N];
set<int> s;
inline sit pre(sit it){return --it;}

inline void Dfs_1(int nw){
    dfn[nw]=++dfc,idf[dfc]=nw;
    siz[nw]=1;dep[nw]=dep[fa[nw]]+1;
    for(node i:E[nw])
    if(i.to^fa[nw]){
        fa[i.to]=nw;dis[i.to]=dis[nw]+i.val;
        Dfs_1(i.to);siz[nw]+=siz[i.to];
        if(siz[i.to]>siz[son[nw]])son[nw]=i.to;
    }
}
inline void Dfs_2(int nw){
    if(!tp[nw])tp[nw]=nw;
    if(son[nw]){tp[son[nw]]=tp[nw];Dfs_2(son[nw]);}
    for(node i:E[nw])
    if(i.to^fa[nw]&&i.to^son[nw])
        Dfs_2(i.to);
}
inline ll Getdis(int x,int y){
    if(!x||!y)return 0;
    ll ans=0;
    while(tp[x]^tp[y]){
    if(dep[tp[x]]>=dep[tp[y]])ans+=dis[x]-dis[fa[tp[x]]],x=fa[tp[x]];
    else ans+=dis[y]-dis[fa[tp[y]]],y=fa[tp[y]];
    }
    ans+=abs(dis[x]-dis[y]);
    return ans;
}

int main(){
    n=read(),m=read();
    for(int t=1;t<n;++t){
    int x=read(),y=read(),z=read();
    E[x].push_back((node){y,z});E[y].push_back((node){x,z});
    }
    Dfs_1(1);Dfs_2(1);
    ll ans=0;
    for(int i=1;i<=m;++i){
    int x=dfn[read()];
    if(!s.size())vis[idf[x]]=1,s.insert(x);
    else{
        sit it=s.lower_bound(x);
        int y=idf[*(it=(it==s.begin()?pre(s.end()):pre(it)))];
        it=s.upper_bound(x);
        int z=idf[*(it=(it==s.end()?s.begin():it))];
        x=idf[x];
        ll d=Getdis(x,y)+Getdis(x,z)-Getdis(y,z);
        if(!vis[x])ans+=d,s.insert(dfn[x]);
        else ans-=d,s.erase(s.find(dfn[x]));
        vis[x]^=1;
    }
    printf("%lld\n",ans);
    }
    return 0;
}

 

[SDOI2015]寻宝游戏

标签:lse   vector   find   ==   --   dig   ++   name   print   

原文地址:https://www.cnblogs.com/fruitea/p/12080249.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!