当前位置: 首页 > news >正文

bzoj3991 LCA + set

https://www.lydsy.com/JudgeOnline/problem.php?id=3991

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

 

 

很显然问题的关键在于处理每一次变更的宝藏点的信息,一个容易发现的结论是,只要是从宝藏点出发,无论哪个点出发都能寻找到一条最优的路径,因为行走的路径会是一个环,对于K个宝藏点,行走的路径是1->2,2->3,3->4.........k-1 -> k,k ->1,所以说,对于一条链,每一次变更只要找到这个点插入的前驱pre和后继nxt,插入的时候删除dis(pre,nxt),加上dis(pre,x) + dis(x,pre)就可以了,删除同理。

问题在于怎么去寻找他的前驱和后继,对于一棵树来说,想要遍历所有的关键点,贪心的想到是和dfs一样走,可以最短的经过一圈所有的关键点,所以我们掏出一手dfs序的前序,对于每一个数字的更改,去寻找他在dfs序里面前后最接近的宝藏点就是他的前驱和后继。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
const int SP = 20;
int N,M,K;
struct Edge{
    int to,next;
    LL dis;
}edge[maxn * 2];
int dfn[maxn];
int head[maxn],tot;
int id;
int idx[maxn];
LL Dis[maxn];
int pa[maxn][SP],dep[maxn];
int vis[maxn];
set<int>Q;
void init(){
    Mem(head,-1);
    tot = 0;
}
void add(int u,int v,LL w){
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].dis = w;
    head[u] = tot++;
}
void dfs(int u,int la){
    dfn[u] = ++id; idx[id] = u;
    pa[u][0] = la;
    For(i,1,SP - 1) pa[u][i] = pa[pa[u][i - 1]][i - 1];
    for(int i = head[u]; ~i; i = edge[i].next){
        int v = edge[i].to;
        if(v == la) continue;
        dep[v] = dep[u] + 1;
        Dis[v] = Dis[u] + edge[i].dis;
        dfs(v,u);
    }
}
int lca(int u,int v){
    if(dep[u] < dep[v]) swap(u,v);
    int t = dep[u] - dep[v];
    For(i,0,SP - 1) if(t & (1 << i)) u = pa[u][i];
    _For(i,SP - 1,0){
        int uu = pa[u][i],vv = pa[v][i];
        if(uu != vv){
            u = uu;
            v = vv;
        }
    }
    return u == v?u:pa[u][0];
}
LL DIS(int u,int v){
    return Dis[u] + Dis[v] - 2 * Dis[lca(u,v)];
}
int main()
{
    Sca2(N,M); init();
    For(i,1,N - 1){
        int u,v; Sca2(u,v);
        LL w; Scl(w);
        add(u,v,w); add(v,u,w);
    }
    Dis[1] = 0;id = 0;dfs(1,1);
    LL ans = 0;
    Q.insert(0); Q.insert(N + 1);
    while(M--){
        int x; Sca(x);
        if(vis[x]){
            int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
            if(pre >= 1) ans -= DIS(idx[pre],x);
            if(pro <= N) ans -= DIS(idx[pro],x);
            if(pre >= 1 && pro <= N) ans += DIS(idx[pre],idx[pro]);
            Q.erase(dfn[x]);
        }else{
            Q.insert(dfn[x]);
            int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
            if(pre >= 1) ans += DIS(idx[pre],x);
            if(pro <= N) ans += DIS(idx[pro],x);
            if(pre >= 1 && pro <= N) ans -= DIS(idx[pre],idx[pro]);
        }
        LL z = 0;
        int s = *++Q.find(0),e = *--Q.find(N + 1);
        if(s >= 1 && e <= N) z = DIS(idx[s],idx[e]);
        Prl(ans + z);
        vis[x] ^= 1;
    }
    #ifdef VSCode
    system("pause");
    #endif
    return 0;
}

 

转载于:https://www.cnblogs.com/Hugh-Locke/p/9762335.html

相关文章:

  • php面相对象基本概念,基本形式,传值
  • 【Linux】- ps 命令
  • 10-序列化
  • EM算法
  • 《弹球学成语》需求分析报告
  • IDEA控制台问题:java lang OutOfMemoryError:PermGen space
  • c语言打印空白星号矩形
  • 关于Qt中窗口的坐标
  • Django将默认的SQLite更换为MySQL
  • Django的contenttypes
  • 离散傅里叶级数DFS
  • NiftyNet开源平台的使用 -- 配置文件
  • 构造代块 的作用
  • [SCOI2010]传送带
  • 2018.10.17多校
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • js操作时间(持续更新)
  • Mac转Windows的拯救指南
  • overflow: hidden IE7无效
  • PAT A1050
  • 初识MongoDB分片
  • 构建二叉树进行数值数组的去重及优化
  • 技术胖1-4季视频复习— (看视频笔记)
  • 聚类分析——Kmeans
  • 面试总结JavaScript篇
  • 如何选择开源的机器学习框架?
  • 如何优雅地使用 Sublime Text
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​业务双活的数据切换思路设计(下)
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (转)IOS中获取各种文件的目录路径的方法
  • (转)linux 命令大全
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)平衡树
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • *p++,*(p++),*++p,(*p)++区别?
  • ... 是什么 ?... 有什么用处?
  • .NET DataGridView数据绑定说明
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • /run/containerd/containerd.sock connect: connection refused
  • @Validated和@Valid校验参数区别
  • [] 与 [[]], -gt 与 > 的比较
  • [].slice.call()将类数组转化为真正的数组
  • [2019.2.28]BZOJ4033 [HAOI2015]树上染色
  • [AIGC] Spring Interceptor 拦截器详解