SDOI2015 寻宝游戏 | noi.ac#460 tree

Posted fengxunling

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDOI2015 寻宝游戏 | noi.ac#460 tree相关的知识,希望对你有一定的参考价值。

题目链接:戳我

可以知道,我们相当于是把有宝藏在的地方围了一个圈,求这个圈最小是多大。
显然按照dfs序来遍历是最小的。

那么我们就先来一遍dfs序列,并且预处理出来每个点到根的距离(这样我们就可用\(dis[u]+dis[v]-2*dis[lca(u,v)]\)来表示u,v之间的距离)

怎么动态维护这个东西呢?平衡树?不存在的,开一个set就行了。每次维护一下添加或者删除产生的影响就行了。

相似的题目是noi.ac#460 tree——

给你一棵n个点的树,每个点都有一个颜色ci。
有m次操作,每次操作会改变一个点的颜色或询问包含某个颜色的所有的点的最小联通块含有的边数。

最小连通块包含的边数,即该题的ans/2。而颜色的数量也不会对时间复杂度产生决定性影响。QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<set>
#include<algorithm>
#define MAXN 1000010
#define ll long long
using namespace std;
int n,m,t,tim;
int head[MAXN];
int dfn[MAXN],low[MAXN],son[MAXN],siz[MAXN],dep[MAXN],fa[MAXN],top[MAXN];
ll ans;
ll dis[MAXN];
set<int>s;
struct Edgeint nxt,to;ll dis;edge[MAXN<<1];
inline void add(int from,int to,ll dis)

    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis;
    head[from]=t;

inline void dfs1(int x,int pre)

    siz[x]=1;
    fa[x]=pre;
    dep[x]=dep[pre]+1;
    int maxx=-1;
    for(int i=head[x];i;i=edge[i].nxt)
    
        int v=edge[i].to;
        if(v==pre) continue;
        dis[v]=dis[x]+edge[i].dis;
        dfs1(v,x);
        siz[x]+=siz[v];
        if(siz[v]>maxx) maxx=siz[v],son[x]=v;
    

inline void dfs2(int x,int topf)

    top[x]=topf;
    dfn[x]=++tim;
    low[tim]=x;
    if(son[x]) dfs2(son[x],topf);
    for(int i=head[x];i;i=edge[i].nxt)
    
        int v=edge[i].to;
        if(v==fa[x]||v==son[x]) continue;
        dfs2(v,v);
    

inline int get_lca(int u,int v)

    while(top[u]!=top[v])
    
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    
    if(dep[u]<dep[v]) return u;
    else return v;

inline ll get_dis(int u,int v)return dis[u]+dis[v]-2*dis[get_lca(u,v)];
int main()

    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    
    dfs1(1,0);
    dfs2(1,1);
    // for(int i=1;i<=n;i++) printf("dis[%d]=%lld\n",i,dis[i]); puts("");
    // for(int i=1;i<=n;i++) printf("dfn[%d]=%d\n",i,dfn[i]); puts("");
    // for(int i=1;i<=n;i++) printf("low[%d]=%d\n",i,low[i]); puts("");
    // for(int i=1;i<=n;i++) printf("dfn[%d]=%d\n",i,dfn[i]); puts("");
    // for(int i=1;i<=n;i++) printf("low[%d]=%d\n",i,low[i]); puts("");
    // for(int i=1;i<=n;i++)
    //  for(int j=i+1;j<=n;j++)
    //      printf("lca[%d %d]=%d\n",i,j,get_lca(i,j));
    set<int>::iterator it,it1,it2;
    while(m--)
    
        int x;
        scanf("%d",&x);
        if(!s.count(dfn[x]))
        
            s.insert(dfn[x]);
            it=it1=it2=s.find(dfn[x]);
            it1--,it2++;
            if(it!=s.begin()) ans+=get_dis(x,low[*it1]);
            if(it2!=s.end()) ans+=get_dis(x,low[*it2]);
            if(it!=s.begin()&&it2!=s.end()) ans-=get_dis(low[*it1],low[*it2]);
        
        else
        
            it=it1=it2=s.find(dfn[x]);
            it1--,it2++;
            if(it!=s.begin()) ans-=get_dis(x,low[*it1]);
            if(it2!=s.end()) ans-=get_dis(x,low[*it2]);
            if(it!=s.begin()&&it2!=s.end()) ans+=get_dis(low[*it1],low[*it2]);
            s.erase(dfn[x]);
        
        if(s.size()<=1)
        
            printf("0\n");
            continue;
        
        it=s.end();--it;
        printf("%lld\n",ans+get_dis(low[*s.begin()],low[*it]));
    
    return 0;

以上是关于SDOI2015 寻宝游戏 | noi.ac#460 tree的主要内容,如果未能解决你的问题,请参考以下文章

P3320 [SDOI2015]寻宝游戏

BZOJ3991: [SDOI2015]寻宝游戏

BZOJ3991[SDOI2015]寻宝游戏 树链的并+set

SDOI2015寻宝游戏

BZOJ3991 [SDOI2015]寻宝游戏

bzoj 3991: [SDOI2015]寻宝游戏