• [SDOI2015]寻宝游戏(LCA,set)


    [SDOI2015]寻宝游戏

    题目描述

    小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。

    小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1:

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

    输出样例#1:

    0
    100
    220
    220
    280

    说明

    1<=N<=100000

    1<=M<=100000

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

    Old Fish 强势秒切 Orz Orz Orz Orz Orz Orz

    手动推一下样例或者自己思考一下可以发现,从任意一个点出发都是等效的

    [ans=dist(a1,a2)+dis(a2,a3)+...+dist(ai-1,ai)+dist(ai,a1) ]

    [1<=i<=m$$**ai按照dfs序从小到大排列** 所以我们可以发现对于每一个点,影响到最终答案的只有它的前趋和后继。所以我们用set维护有宝物的点的dfs序,每次更新答案即可。 ```cpp #include<bits/stdc++.h> #define lll long long #define It set<lll>::iterator using namespace std; lll read() { lll x=0,w=1;char ch=getchar(); while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*w; } const lll N=100010; lll n,m,cnt,visnum,ans,x,y,z; lll head[N],deep[N],dfn[N],pos[N],vis[N],f[N][20],sum[N][20]; set<lll> s; struct node{ lll v,to,next; }edge[2*N]; void add(lll x,lll y,lll z) { cnt++;edge[cnt].to=y;edge[cnt].v=z;edge[cnt].next=head[x];head[x]=cnt; } void dfs(lll k,lll fa) { dfn[k]=++visnum;pos[visnum]=k; for(lll i=head[k];i;i=edge[i].next) { lll v=edge[i].to;if(v==fa) continue; deep[v]=deep[k]+1;f[v][0]=k;sum[v][0]=edge[i].v;dfs(v,k); } } void init() { for(lll i=1;i<=19;i++) for(lll j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1],sum[j][i]=sum[j][i-1]+sum[f[j][i-1]][i-1]; } lll lca(lll x,lll y) { lll qwe=0; if(deep[x]<deep[y]) swap(x,y); for(lll i=19;i>=0;i--) if(deep[f[x][i]]>=deep[y]) qwe+=sum[x][i],x=f[x][i]; if(x==y) return qwe; for(lll i=19;i>=0;i--) if(f[x][i]!=f[y][i]) qwe+=sum[x][i]+sum[y][i],x=f[x][i],y=f[y][i]; return qwe+sum[x][0]+sum[y][0]; } It l(It k) { if(k==s.begin()) return --s.end(); return --k; } It r(It k) { if(k==--s.end()) return s.begin(); return ++k; } void change1(lll k) { It it;lll t; if(s.size()) { it=s.lower_bound(dfn[k]); if(it==s.end()) it=s.begin(); t=*l(it); ans+=lca(k,pos[t])+lca(k,pos[*it])-lca(pos[t],pos[*it]); } s.insert(dfn[k]); } void change2(lll k) { It it;lll t; it=s.find(dfn[k]); t=*l(it);it=r(it); ans-=lca(k,pos[t])+lca(k,pos[*it])-lca(pos[t],pos[*it]); s.erase(dfn[k]); } int main() { n=read();m=read(); for(lll i=1;i<n;i++) { x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } dfs(1,0);init(); for(lll i=1;i<=m;i++) { x=read(); if(!vis[x]) change1(x),vis[x]=1; else change2(x),vis[x]=0; printf("%lld ",ans); } return 0; } ```]

  • 相关阅读:
    下载android的linux内核的方法
    安装sunjava5jdk 提示"无法找到软件包sunjava5jdk
    Linux kernel中的annotation(转)
    2012年计划
    Android 开机图片/文字/动画的修改(转)
    2.6 内核中的计时器和列表
    linux设置默认网关
    谷歌Android被Linux内核除名(转)
    使用call_usermodehelper在Linux内核中直接运行用户空间程序(转)
    S3C2410平台上运行为例,讲解内核的解压过程
  • 原文地址:https://www.cnblogs.com/lsgjcya/p/9307637.html
Copyright © 2020-2023  润新知