BZOJ 3991 [SDOI 2015] 寻宝游戏 / Acwing 355. 异象石
从第一天下午调到第二天下午……蒟蒻这么菜是有原因的。
题目链接
Acwing 355. 异象石
BZOJ 3991: [SDOI2015]寻宝游戏
Luogu P3320 [SDOI2015]寻宝游戏
除输入不同,答案是否除以二不同外,三题几乎一样。蒟蒻做的是第一道,故按照第一题讲解。
按dfs遍历顺序维护出每个点的时间戳——即第一次到达该点的时刻,每到一个点时间戳就加1——存入dfn数组。答案ans随每一次操作(异象石出现、消失)而更新。
要求出树上几个点之间连边的总长度,可按dfn顺序求出相邻点之间的路径长度之和,如图(为方便,我将节点编号设为它的dfn值):

假设在4、2、5、6点出现了异象石(用灰色填充),我们要按照dfn顺序,将它们相邻两点间的距离全部加起来。
注意:dfn最大与最小的两个点之间的距离也要加上(可想象成环)。
(图丑且繁琐,见谅)

先从2到4。

再从4到5。

再从5到6。

关键:加上6到2之间的距离。我掉过的坑
此时我们发现,这四个点之间的边都刚好被经过了2次,我们便可以此为突破口,将其统计入答案,输出时将答案除以2即可。
要加点(即出现新的异象石)时,答案首先将dfn在它之前和之后的两个节点间距离减掉,再加上它与前一个点的距离和后一个点的距离即可。删点(异象石消失)以此类推。注意,当该点dfn最小时,它的上一个点可看作dfn最大的那个点;dfn最大时,它的下一个点可看作dfn最小的那个点。参照前文中“dfn最大与最小的两个点之间的距离也要加上(可想象成环)”。又一个掉过的坑
要让出现了异象石的点按dfn值有序,可运用stl:set维护,定义结构体存储每个点的id(即点的序号)、ts(即时间戳),并重载运算符,按ts排序即可。
set的用法——by 知足–常乐
这又是一个我掉过的……都不能叫做坑了,是自己不会用set而已。其实set非常实用,可维护序列顺序,还可较为方便地取出、删除元素。要知道我以前有多少次做题时渴望这样一个容器啊QAQ,今天终于好像会用了。就是指针有点坑……
还有一个坑细节是:“节点的深度”与“节点离根节点的距离”要分别用dep,dist两个数组存储。我原先曾犯过这样的错误,根节点的深度要赋为1,否则若赋为0,求最近公共祖先lca时会跳的过高。
注意开long long !!!
#include
using namespace std;typedef long long ll;
const int maxn=100100;
int n,cnt,tim,m;
int head[maxn],f[maxn][35],dfn[maxn];
ll dist[maxn],dep[maxn];struct node
{int id,ts;char c;bool operator <(const node a) const {return ts<a.ts;}
};set<node>s;struct edge
{int v,nxt,w;
}e[maxn<<1];void add(int u,int v,int w)
{e[++cnt].nxt=head[u];e[cnt].v=v;e[cnt].w=w;head[u]=cnt;
}void dfs(int u,int fa)
{dfn[u]=++tim;dep[u]=dep[fa]+1;for(int i=1;(1<<i)<=dep[u];i++){f[u][i]=f[f[u][i-1]][i-1];}for(int i=head[u];i;i=e[i].nxt){int v=e[i].v,w=e[i].w;if(v==fa) continue;f[v][0]=u;dist[v]=dist[u]+w;dfs(v,u);}
}int lca(int x,int y)
{if(dep[x]<dep[y]) swap(x,y);for(int i=30;i>=0;i--){if(dep[f[x][i]]>=dep[y]) x=f[x][i];}if(x==y) return x;for(int i=30;i>=0;i--)if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}return f[x][0];
}node getp(set<node>::iterator x)
{if(x==s.begin()) return (*(--s.end()));return *(--x);
}node getn(set<node>::iterator x)
{if(x==--s.end()) return *(s.begin());return *(++x);
}ll dis(int x,int y)//树上差分求两点间距离
{return (ll)dist[x]+dist[y]-2*dist[lca(x,y)];
}int main()
{//freopen("input.txt","r",stdin);scanf("%d",&n);char ch;int x,y,z,st,t;for(int i=1;i<n;i++){scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}ll ans=0;dfs(1,0);scanf("%d",&m);for(int i=1;i<=m;i++){do{ch=getchar();}while(ch!='+'&&ch!='?'&&ch!='-');if(ch=='?'){printf("%lld\n",ans/2);continue;}scanf("%d",&y);if(ch=='+') {s.insert((node){y,dfn[y]});set <node> :: iterator it=s.find((node){y,dfn[y]});int pre=(getp(it)).id;//dfn小于该点的第一个节点int nxt=(getn(it)).id;//dfn大于该点的第一个节点ans+=dis(((node)*it).id,pre)+dis(((node)*it).id,nxt)-dis(pre,nxt);}else if(ch=='-'){s.insert((node){y,dfn[y]});set <node> :: iterator it=s.find((node){y,dfn[y]});int pre=(getp(it)).id;int nxt=(getn(it)).id;ans-=dis(((node)*it).id,pre)+dis(((node)*it).id,nxt)-dis(pre,nxt);s.erase(it);}}return 0;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
