异象石——最近公共祖先

题目链接:

https://www.acwing.com/problem/content/357/

题意:

  给出一个树上节点的集合,动态加入或者删除节点,询问连通所有节点的最小边集的权值之和。

解法:

  把集合中的节点按照时间戳排序,每相邻两个节点距离以及首尾节点距离累加,累加之和为答案的两倍。(目前不会证明)

节点排序用set维护,首先加入0和inf,以便修改。

代码:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int N=100000+5;
  5 const int INF=N-1;
  6
  7 int n,m;
  8
  9 int head[N],ver[2*N],edge[2*N],nex[2*N],tot=1;
 10 inline void add(int x,int y,int z)
 11 {
 12     ver[++tot]=y;
 13     edge[tot]=z;
 14     nex[tot]=head[x];
 15     head[x]=tot;
 16 }
 17
 18 int d[N],f[N][20],max_t;
 19 ll dis[N];
 20 void bfs()
 21 {
 22     max_t=log(n)/log(2)+1;
 23     d[1]=1;
 24     dis[1]=0;
 25     queue<int> q;
 26     q.push(1);
 27     while(!q.empty())
 28     {
 29         int x=q.front();
 30         q.pop();
 31         for(int i=head[x];i;i=nex[i])
 32         {
 33             int y=ver[i];
 34             if(d[y])continue;
 35             d[y]=d[x]+1;
 36             dis[y]=dis[x]+edge[i];
 37             f[y][0]=x;
 38             for(int i=1;i<=max_t;++i)
 39                 f[y][i]=f[f[y][i-1]][i-1];
 40             q.push(y);
 41         }
 42     }
 43 }
 44 int lca(int x,int y)
 45 {
 46     if(d[x]<d[y])swap(x,y);
 47     if(d[x]>d[y])
 48         for(int i=max_t;i>=0;--i)
 49             if(d[f[x][i]]>=d[y])
 50                 x=f[x][i];
 51     if(x==y)return x;
 52     for(int i=max_t;i>=0;--i)
 53         if(f[x][i]!=f[y][i])
 54             x=f[x][i],y=f[y][i];
 55     return f[x][0];
 56 }
 57
 58 int dfn[N],to_node[N],tot2=0;
 59 void dfs(int x)
 60 {
 61     dfn[x]=++tot2;
 62     to_node[tot2]=x;
 63     for(int i=head[x];i;i=nex[i])
 64     {
 65         int y=ver[i];
 66         if(dfn[y])continue;
 67         dfs(y);
 68     }
 69 }
 70
 71 inline ll dist(int x,int y)
 72 {
 73     if(x==0||y==0)
 74         return 0;
 75     return dis[x]+dis[y]-2*dis[lca(x,y)];
 76 }
 77
 78 int main()
 79 {
 80     scanf("%d",&n);
 81     for(int i=1;i<n;++i)
 82     {
 83         int x,y,z;
 84         scanf("%d%d%d",&x,&y,&z);
 85         add(x,y,z);
 86         add(y,x,z);
 87     }
 88     bfs();
 89     dfs(1);
 90     scanf("%d",&m);
 91     char op;
 92     ll ans=0;
 93     set<int> s;
 94     s.insert(0);
 95     s.insert(INF);
 96     for(int i=0;i<m;++i)
 97     {
 98         scanf(" %c",&op);
 99         if(op==‘+‘)
100         {
101             int x;
102             scanf("%d",&x);
103             auto r=s.insert(dfn[x]);
104             auto it=r.first,it1=it,it2=it;
105             --it1;++it2;
106             ans-=dist(to_node[*it1],to_node[*it2]);
107             ans+=dist(to_node[*it1],to_node[*it]);
108             ans+=dist(to_node[*it2],to_node[*it]);
109         }
110         else if(op==‘-‘)
111         {
112             int x;
113             scanf("%d",&x);
114             auto it=s.find(dfn[x]),it1=it,it2=it;
115             --it1;++it2;
116             ans+=dist(to_node[*it1],to_node[*it2]);
117             ans-=dist(to_node[*it1],to_node[*it]);
118             ans-=dist(to_node[*it2],to_node[*it]);
119             s.erase(it);
120         }
121         else
122         {
123             auto it1=s.begin();
124             auto it2=s.rbegin();
125             ++it1;++it2;
126             printf("%lld\n",(ans+dist(to_node[*it1],to_node[*it2]))/2);
127         }
128     }
129     return 0;
130 }

原文地址:https://www.cnblogs.com/judp/p/11230863.html

时间: 2024-11-02 09:57:20

异象石——最近公共祖先的相关文章

【NOIP模拟赛】异象石

Description Adera是Microsoft应用商店中的一款解谜游戏. 异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图.这张地图上有N个点,有N-1条双向边把它们连通起来.起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一: 1.地图的某个点上出现了异象石(已经出现的不会再次出现): 2.地图某个点上的异象石被摧毁(不会摧毁没有异象石的点): 3.向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少. 请你作为玩家回答这

Luogu P3320 [SDOI2015]寻宝游戏 / 异象石 【LCA/set】

期末考试结束祭! 在期末考试前最后一发的测试中,异象石作为第二道题目出现QAQ.虽然知道是LCA图论,但还是敲不出来QAQ. 花了两天竞赛课的时间搞懂(逃 异象石(stone.pas/c/cpp)题目描述Adera 是 Microsoft 应用商店中的一款解谜游戏.异象石是进入 Adera 中异时空的引导物,在 Adera 的异时空中有一张地图.这张地图上有 N 个点,有 N-1 条双向边把它们连通起来.起初地图上没有任何异象石,在接下来的 M个时刻中,每个时刻会发生以下三种类型的事件之一:1.

50、树中两个节点的公共祖先

详细的询问: 1.该树是二叉查找树? 最近公共祖先----二叉查找树:(http://www.lintcode.com/problem/lowest-common-ancestor/) 思路:利用左子树特点:左子树 < 根 <= 右,输入节点跟根节点比较,都小于,在左子树,都大约右子树,递归的去遍历:找到当前节点在两个输入大小之间,当前节点就是. 递归和非递归 public class Solution { public TreeNode lowestCommonAncestor(TreeNo

D5 LCA 最近公共祖先

第一题: POJ 1330 Nearest Common Ancestors POJ 1330 这个题可不是以1为根节点,不看题就会一直wa呀: 加一个找根节点的措施: #include<algorithm> #include<bitset> #include<cctype> #include<cerrno> #include<clocale> #include<cmath> #include<complex> #incl

【洛谷P3379】【模板】最近公共祖先(LCA)

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 5 5 4 3 1 2 4 5

[最近公共祖先] POJ 3728 The merchant

The merchant Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 4556   Accepted: 1576 Description There are N cities in a country, and there is one and only one simple path between each pair of cities. A merchant has chosen some paths and w

lca最近公共祖先(st表)

大体思路 1.求出每个元素在树中的深度 2.用st表预处理的方法处理出f[i][j],f[i][j]表示元素i上方第2^j行对应的祖先是谁 3.将较深的点向上挪,直到两结点的深度相同 4.深度相同后,祖先可能就在上方,再走几步就到了,于是两个点同时向上移 具体的方法和代码贴在下面 ↓ 具体来看 1.求出每个元素在树中的深度 //求每个节点在树中的深度 void dfs(int pos,int pre)//pre是pos的父节点 { for(int i=0;i<v[pos].size;i++)//

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问