您的位置:首页 > 其它

BZOJ 3991 SDOI 2015 寻宝游戏(异象石) LCA + Set + DFS序

2015-10-23 15:53 260 查看
异象石

(stone.pas/c/cpp)

题目描述

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 上的异象石被摧毁

? 表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。

输出格式

对于每个 ? 事件,输出一个整数表示答案。

样例输入

6

1 2 1

1 3 5

4 1 7

4 5 3

6 4 2

10

+ 3

+ 1

?

+ 6

?

+ 5

?

- 6

- 3

?

样例输出

5

14

17

10

数据范围与约定

对于 30%的数据,1 ≤ n, m ≤ 1000。

对于另 20%的数据,地图是一条链,或者一朵菊花。

对于 100%的数据,1 ≤ n, m ≤ 10^5

, 1 ≤ x, y ≤ n, x ≠ y, 1 ≤ z ≤ 10^9。

这两道题是一模一样的题,只不过输入格式改了一下,然后异象石这道题输出的时候把答案除以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 <cstring>
#include <algorithm>
#include <vector>
#include <set>
#define M 100005
using namespace std;
typedef long long L;

struct Edge{int u, v, w;};
struct node{
int id, num;
bool operator < (node k) const
{
return num < k.num;
}
};

int n, m, num[M], d[M], F[M][20];
bool app[M];
L ans, D[M][20];
Edge E[M<<1];

vector <int> G[M];
set <node> Set;

node make_node(int k)
{
node t; t.id = k, t.num = num[k];
return t;
}

void get(int &x)
{
char c = getchar(); x = 0;
while(c < '0' || c > '9') c = getchar();
while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}

void init()
{
scanf("%d %d", &n, &m);
for(int i = 1; i < n; i++)
{
int u, v, w; get(u); get(v); get(w);
E[i*2-1].u = u, E[i*2].u = v;
E[i*2-1].v = v, E[i*2].v = u;
E[i*2-1].w = w, E[i*2].w = w;
G[u].push_back(i*2-1);
G[v].push_back(i*2);
}
}

void dfs(int u, int fa, int x)
{
num[u] = x;
F[u][0] = fa;
d[u] = d[fa] + 1;

for(int i = 1; (1<<i) <= d[u]; i++)
{
F[u][i] = F[F[u][i-1]][i-1];
D[u][i] = D[u][i-1] + D[F[u][i-1]][i-1];
}

for(int i = 0; i < G[u].size(); i++)
{
int e = G[u][i];
if(fa != E[e].v)
{
D[E[e].v][0] = E[e].w;
dfs(E[e].v, u, ++num[0]);
}
}
}

L lca(int a, int b)
{
L res = 0;
if(d[a] < d[b]) swap(a, b);
while(d[a] != d[b])
{
int k = 18;
while(d[F[a][k]] < d[b] && k) k--;
res += D[a][k];
a = F[a][k];
}
while(a != b)
{
int k = 18;
while(F[a][k] == F[b][k] && k) k--;
res += D[a][k] + D[b][k];
a = F[a][k], b = F[b][k];
}
return res;
}

int Left(int x)
{
set<node>::iterator i = Set.lower_bound(make_node(x));
if(i == Set.begin()) i = Set.end();
return (*--i).id;
}

int Right(int x)
{
set<node>::iterator i = Set.lower_bound(make_node(x));
if((++i) == Set.end()) return (*Set.begin()).id;
return (*i).id;
}

L query(int x)
{
int las = 0, nxt = 0, flag;
if(!app[x]){
flag = 1;
Set.insert(make_node(x));
las = Left(x), nxt = Right(x);
}
else{
flag = -1;
las = Left(x), nxt = Right(x);
Set.erase(make_node(x));
}
app[x] = !app[x];
if(Set.size() <= 1) return ans = 0;
ans += flag*(lca(nxt, x) + lca(las, x) - lca(las, nxt));
return ans;
}

int main()
{
init();
dfs(1, 0, ++num[0]);
while(m--)
{
int x; get(x);
printf("%lld\n", query(x));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj