被指针折磨得死去活来......
所以这里具体分析指针
题目描述
Adera 是 Microsoft 应用商店中的一款解谜游戏。
异象石是进入 Adera 中异时空的引导物,在 Adera 的异时空中有一张地图。这张地图上
有 N 个点,有 N-1 条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的 M
个时刻中,每个时刻会发生以下三种类型的事件之一:
1. 地图的某个点上出现了异象石(已经出现的不会再次出现);
2. 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
3. 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。
输入格式
第一行有一个整数 N,表示点的个数。
接下来 N-1 行每行三个整数 x,y,z,表示点 x 和 y 之间有一条长度为 z 的双向边。
第 N+1 行有一个正整数 M。
接下来 M 行每行是一个事件,事件是以下三种格式之一:
+ x 表示点 x 上出现了异象石
- x 表示点 x 上的异象石被摧毁
? 表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
输出格式
对于每个 ? 事件,输出一个整数表示答案。
先看思路:
https://blog.csdn.net/qq_25978793/article/details/49362505
这两道题是一模一样的题,只不过输入格式改了一下,然后异象石这道题输出的时候把答案除以2。不过我最初做的是异象石这道题,所以就按照这道题来解释了。
题目大意就是,给一棵树,要求动态维护一个值:使所选出的点连通的边的最小权值和(最小的意思就是不需要更多的边,已经达到连通状态,因为是树,所以这个边集是唯一的)。有加入和删除所选定的点的操作。
如图,对于当前这棵树:
我们选择的节点为图中标号1、2、3的三个节点,那么√标注的边的权值和就是我们所求的。设lca(a, b)为a,b到两个点最近公共祖先的路径长度之和。那么,这些边的权值和就是(lca(1,2)+lca(2,3)+lca(3,1))/2。并发现这个和是这样子的:
这一圈,就是寻宝游戏的答案,就是异象石的答案乘以2的结果。可以想到,对于这样的点集,要求的这个“圈”,可以通过两两点之间的lca加和得到。对于一个点的序列a[1]~a[i],就是lca(a[1],a[2])+lca(a[2],a[3])+……+lca(a[i-1],a[i])+lca(a[i],1)。而这个序列点的顺序是什么?DFS序。画一画图很容易就明白了。
所以,我们先预处理出这棵树节点的DFS序的序号(就是在DFS过程中,这个点是第几个被访问到的)。在维护的过程中,假设加入一个点后,它的位置是i,那么对于答案ans,我们需要做的改变就是ans += lca(a[i-1],a[i])+lca(a[i],a[i+1])-lca(a[i-1],a[i+1]);这个也很容易明白吧。相反,如果删除一个点,它原本的位置是i,只需要ans -= lca(a[i-1],a[i])+lca(a[i],a[i+1])-lca(a[i-1],a[i+1]);【切记不要每次修改只搞一个bool数组记录而每次询问都全部重新计算,而是要动态维护ans】
那么用什么来做到维护a序列呢?STL里的set就可以。s.insert(x);插入x, s.erase(x);删除x, s.lower_bound(x);返回一个迭代器,指向集合中第一个大于或等于x的元素。集合键值就是DFS序的编号,用这些函数就可以维护a序列,并且求出a[i],a[i-1],a[i+1]是哪些节点。
上自己的代码:
#include<cstdio> #include<cstdlib> #include<iostream> #include<set> #define ll long long using namespace std; inline int read() { int x=0;char c=getchar(); while(c<'0' || c>'9') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x; } int n,m; const int N=1e5+3; int tot,head[N]; struct node {int v,nx; ll w;}e[N<<1]; void add(int u,int v,ll w) { e[++tot].v =v,e[tot].w =w,e[tot].nx =head[u],head[u]=tot; e[++tot].v =u,e[tot].w =w,e[tot].nx =head[v],head[v]=tot; } int dep[N],cnt,dfn[N],bac[N]; int fa[N][18];ll pth[N][18]; void dfs1(int rt,int f) { dep[rt]=dep[f]+1; dfn[rt]=++cnt,bac[cnt]=rt; for(int i=1;i<18 && fa[rt][i-1]>0 ;i++) fa[rt][i]=fa[fa[rt][i-1]][i-1], pth[rt][i]=pth[rt][i-1]+ pth[fa[rt][i-1]][i-1]; for(int i=head[rt];i;i=e[i].nx ) { int v=e[i].v ; if(v==f) continue; fa[v][0]=rt,pth[v][0]=e[i].w ; dfs1(v,rt); } } ll ans; set <int> s; ll LCA(int xx,int yy) { ll sum=0; if(dep[xx] < dep[yy]) swap(xx,yy); int dis=dep[xx]-dep[yy]; for(int i=1,j=0;i<=dis;i<<=1,j++) if((dis&i)>0) sum+=pth[xx][j],xx=fa[xx][j]; if(xx==yy) return sum; for(int i=17;i>=0;i--) if(fa[xx][i]!=fa[yy][i]) sum+=pth[xx][i]+pth[yy][i],xx=fa[xx][i],yy=fa[yy][i]; return sum+pth[xx][0]+pth[yy][0]; } int main() { n=read(); for(int i=1;i<n;i++) { int u=read(),v=read();ll w=read(); add(u,v,w); } dfs1(1,0); m=read(); char ch; while(m--) { cin>>ch; if(ch=='?') cout<<ans/2<<endl; else if(ch=='+') { int find_x=read(); if(s.empty() ) { s.insert(dfn[find_x]); continue; } set<int> ::iterator nx=s.upper_bound(dfn[find_x]),pre=nx; --pre; if(nx==s.end() ) nx=s.begin() ; if(nx==s.begin() ) pre=s.end() ,pre--; int u=bac[*pre],v=bac[*nx]; //加上一个*就可以当值使用 ans+=LCA(u,find_x)+LCA(find_x,v)-LCA(u,v); s.insert(dfn[find_x]); } else if(ch=='-') { int find_x=read(); set<int> ::iterator pos=s.lower_bound( dfn[find_x] ),pre=pos,nx=pos; if(pos==s.end() ) continue; --pre,++nx; if(nx==s.end() ) nx=s.begin() ; if(pos==s.begin() ) pre=s.end() ,--pre; int u=bac[*pre],v=bac[*nx]; //加上一个*就可以当值使用 ans-=LCA(u,find_x)+LCA(find_x,v)-LCA(u,v); s.erase(pos); } } return 0; }
(1)使用s.lower_bound((int)x),的时候,返回的位置要用
set<int> ::iterator it 记录
(2)返回结果可能为s.begin(),s.end(),
如果要获取前一个和后一个,都要讨论,漏一个就re
(3)位置前写*,可以直接当int使用
(4)不可以写什么pos+1,只能++或者--