zoukankan      html  css  js  c++  java
  • [SDOI2015][BZOJ3991] 寻宝游戏|set|dfs序|虚树|树上倍增LCA

    3991: [SDOI2015]寻宝游戏

    Time Limit: 40 Sec  Memory Limit: 128 MB
    Submit: 427  Solved: 212
    [Submit][Status][Discuss]

    Description

     小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物

     

    Input

     第一行,两个整数N、M,其中M为宝物的变动次数。

    接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。
    接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。
     

    Output

     M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。

     

    Sample Input

    4 5
    1 2 30
    2 3 50
    2 4 60
    2
    3
    4
    2
    1

    Sample Output

    0
    100
    220
    220
    280

    HINT

     1<=N<=100000


    1<=M<=100000

    对于全部的数据,1<=z<=10^9

    Source

    给定一棵树,每次将某个点设为关键点或取消关键点,求虚树中边长总和的二倍

    插入or删除一个结点时就把其dfs序在set中插入or删除

    每次答案就是当前set中相邻结点的距离和,再加上根到最后一个结点的距离

    第一次用set……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #define N 100005
    #define inf 2000000000
    #define ll long long
    using namespace std;
    set<int> s;
    int n,m,cnt,dfn;
    int fa[N][20],deep[N],head[N],id[N],pos[N];
    ll ans,d[N];
    bool mark[N];
    int next[2*N],list[2*N],key[2*N];
    inline int read()
    {
        int a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    inline void insert(int x,int y,int z)
    {
        next[++cnt]=head[x];
        head[x]=cnt;
        list[cnt]=y;
        key[cnt]=z;
    }
    void dfs(int x)
    {
        id[x]=++dfn; pos[dfn]=x;
        for (int i=1;(1<<i)<=deep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
        for (int i=head[x];i;i=next[i])
            if (list[i]!=fa[x][0])
            {
                fa[list[i]][0]=x;
                deep[list[i]]=deep[x]+1;
                d[list[i]]=d[x]+key[i];
                dfs(list[i]);
            }
    }
    inline int lca(int x,int y)
    {
        if (deep[x]<deep[y]) swap(x,y);
        int t=deep[x]-deep[y];
        for (int i=0;(1<<i)<=t;i++) 
            if ((1<<i)&t) x=fa[x][i];
        for (int i=19;i>=0;i--)
            if (fa[x][i]!=fa[y][i]) {x=fa[x][i]; y=fa[y][i];}
        return x==y?x:fa[x][0];
    }
    inline ll dis(int x,int y)
    {
        return d[x]+d[y]-2*d[lca(x,y)];
    }
    int main()
    {
        n=read(); m=read();
        for (int i=1;i<n;i++)
        {
            int u=read(),v=read(),w=read();
            insert(u,v,w); insert(v,u,w);
        }
        dfs(1);
        s.insert(inf); s.insert(-inf);
        ll tmp;
        for (int i=1;i<=m;i++)
        {
            int x=read(),f=1;
            if (mark[x]) {f=-1; s.erase(id[x]);} else s.insert(id[x]);
            mark[x]^=1;
            int l=*--s.lower_bound(id[x]),r=*s.upper_bound(id[x]);
            if (r!=inf) ans+=dis(x,pos[r])*f;
            if (l!=-inf) ans+=dis(x,pos[l])*f;
            if (l!=-inf&&r!=inf) ans-=f*dis(pos[l],pos[r]);
            if (s.size()!=2)
                tmp=dis(pos[*s.upper_bound(-inf)],pos[*--s.lower_bound(inf)]);
            else tmp=0;
            printf("%lld
    ",ans+tmp);
        }
        return 0;
    }
  • 相关阅读:
    【程序员面试宝典】第五章 程序设计基本概念
    win7打开或关闭windows功能 提示“出现错误,并非所有的功能被更改”,管理员权限惹的祸
    堆排序
    目态与管态的概念
    循环不变式的概念
    getchar()函数的返回值赋给char型,用if(ch=getchar() != EOF)测试,输入ctrl+z同样可以结束循环的分析
    java算法 -- 冒泡排序
    Java算法 -- 二分查找
    Sql知识点总结
    java实现 链表反转
  • 原文地址:https://www.cnblogs.com/ws-fqk/p/4731106.html
Copyright © 2011-2022 走看看