码迷,mamicode.com
首页 > Windows程序 > 详细

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

时间:2019-06-15 13:49:30      阅读:136      评论:0      收藏:0      [点我收藏+]

标签:define   i++   using   size   online   cpp   影响   class   lin   

题目链接:戳我

可以知道,我们相当于是把有宝藏在的地方围了一个圈,求这个圈最小是多大。
显然按照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 Edge{int 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

标签:define   i++   using   size   online   cpp   影响   class   lin   

原文地址:https://www.cnblogs.com/fengxunling/p/11027155.html

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