[OpenJudge0054]特务会议召开

试题描述

在敌占区的特务时常会碰头。敌占区有n个城市,为保证安全,中央经过侦查,只选择了n-1条较安全的道路作为特务们碰头可以走的道路。每次开会,中央会选择特务正在工作当中的最早开始工作的那一个所在城市作为开会地点。为保证特务安全,开会时,中央会派安全小队在每一条特务会经过的道路上进行保护。每次开会时,中央想知道需要保护的道路共有多少条。

有时中央会认为一个城市很重要,于是在这个城市安插一个特务(即使该城已有特务);有时一个城市的敌人被干掉了,于是召回这个城市的特务(即使城中没有特务)。一开始中央还没有向敌占区派特务。

输入

第一行两个整数n,m,表示城市数和中央的命令数。
接下来n-1行,每行两个整数x和y,表示x和y间有一条特务可以通过的道路。
接下来m行,每行开头一个字符:
若为 + ,接下来一个数w,表示中央在w安插了一名特务。
若为 - ,接下来一个数w,表示城市w的特务全被召回。
若为 ?,表示特务会议召开,问有多少道路需要被保护。
(+,-与数之间有空格)

输出

对于每次特务会议召开,输出一行一个整数,表示需要被保护的道路数。

输入示例

3 6
1 2
1 3
+ 2
+ 1
+ 3
?
- 2
?

输出示例

2
1

数据规模及约定

对于30%的数据,n<=1000,m<=1000;
对于100%的数据,n<=100000,m<=100000;

题解

借这道题学一下虚树的概念和性质。

不难发现这题我们只需要关心安有特务的点(不妨称之为特殊点),好的,那么我们将这些点提取出来,我们还需要关心他们的 lca,但是两两求 lca 显然是 n2 的;而我们又发现如果将这些点按照它们在树上的 dfs 序大小排个序,找相邻两个点的 lca 就好了。没错,这些 lca 和特殊点就构成了虚树。

性质 1:有不超过 2k 个节点(k 是特殊点个数)。显然 lca 个数小于 k,加起来小于 2k。

性质 2:相邻两个点的距离之和(包括第 1 个和最后一个特殊点)等于虚树大小的一半。这个也很显然自己,画画图就好了。

有了性质 2 这题就做完了。

搞个 set,懒得写平衡树了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <set>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 100010
#define maxm 200010
#define maxlog 20
int n, q, m, head[maxn], nxt[maxm], to[maxm];

void AddEdge(int a, int b) {
	to[++m] = b; nxt[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; nxt[m] = head[a]; head[a] = m;
	return ;
}

int Dfs[maxn], clo, Poi[maxn], Fa[maxlog][maxn], dep[maxn];
void build(int u, int fa) {
	Dfs[u] = ++clo; Poi[clo] = u;
	Fa[0][u] = fa;
	for(int i = 1; i < maxlog; i++) Fa[i][u] = Fa[i-1][Fa[i-1][u]];
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
		dep[to[e]] = dep[u] + 1;
		build(to[e], u);
	}
	return ;
}

int calc(int a, int b) {
//	printf("in calc: %d %d\n", a, b);
	int ans = dep[a] + dep[b];
	if(dep[a] < dep[b]) swap(a, b);
	for(int i = maxlog - 1; i >= 0; i--)
		if(dep[a] - dep[b] >= (1 << i)) a = Fa[i][a];
	for(int i = maxlog - 1; i >= 0; i--)
		if(Fa[i][a] != Fa[i][b]) a = Fa[i][a], b = Fa[i][b];
	int c = a == b ? a : Fa[0][b];
	ans -= (dep[c] << 1);
	return ans;
}

set <int> S;
std :: set <int> :: iterator it, it2;

int main() {
	n = read(); q = read();
	for(int i = 1; i < n; i++) {
		int a = read(), b = read();
		AddEdge(a, b);
	}

	build(1, 0);
//	for(int i = 1; i <= n; i++) printf("(%d)%d ", i, Dfs[i]); putchar(‘\n‘);
//	for(int i = 1; i <= n; i++) printf("%d ", Poi[i]); putchar(‘\n‘);
	int ans = 0;
	while(q--) {
		char op[2]; scanf("%s", op);
		if(op[0] == ‘+‘) {
			int u = read();
			if(S.count(Dfs[u])) continue;
			if(S.size()) {
				it = S.upper_bound(Dfs[u]);
				int a = *it, b;
				if(it == S.end()) a = *(S.begin());
				if(it == S.begin()) {
					it2 = S.end();
					b = *(--it2);
				}
				else b = *(--it);
				a = Poi[a]; b = Poi[b];
				ans -= calc(a, b);
				ans += calc(a, u); ans += calc(u, b);
			}
			S.insert(Dfs[u]);
		}
		if(op[0] == ‘-‘) {
			int u = read();
			if(!S.count(Dfs[u])) continue;
			if(S.size()) {
				it = S.upper_bound(Dfs[u]);
				int a = *it, b;
				if(it == S.end()) a = *(S.begin());
				it = S.lower_bound(Dfs[u]);
				if(it == S.begin()) {
					it2 = S.end();
					b = *(--it2);
				}
				else {
					it2 = it;
					b = *(--it2);
				}
				a = Poi[a]; b = Poi[b];
				ans += calc(a, b);
				ans -= calc(a, u); ans -= calc(u, b);
			}
			S.erase(Dfs[u]);
		}
		if(op[0] == ‘?‘) printf("%d\n", ans >> 1);
	}

	return 0;
}
/*
13 1000
1 2
1 3
1 4
1 5
2 6
2 7
6 8
6 9
6 10
4 11
5 12
5 13
?
+ 13
?
+ 11
?
+ 9
?
*/
时间: 2025-01-01 09:59:24

[OpenJudge0054]特务会议召开的相关文章

【第四组】第九次冲刺会议

梁:做了:学习排序用法 问题:学习进度缓慢 规划:继续学习实现张:做了:实现js和webview的信息交互 问题:交互时时间控制不好设置为动态 规划:镜头转换,分支合并,路径得出邱:做了:学习标签点击事件 问题:需要得到这个点是否具有是标签 规划:完成标签点击事件武:做了:界面做了修改 问题:编译报错 规划:学习动态界面设计 孔:人出差 诸:做了:完成了设置界面的文件夹的选择,首页图片显示 问题:时间不够 规划:做按时间地点分类界面 commit: 进度: 会议图片:

第二轮冲刺-Runner站立会议04

一.会议细节 时间 2016/5/26 地点 基教 二.会议记录 姓名 今天做了什么 明天要做什么 遇到的问题 李可 查询图标,优化图标 继续查询图标,优化图标 暂无 毛雯雯 添加日期和图标 继续完成今天的任务 没有做完 张更 实现了查询界面和日历界面之间互相传值,可以选择日期来显示对应日期的消费明细 完善并美化查询界面,如果有时间再美化扇形图统计界面. 在日历界面传值时出现了NullPointerException的空指针错误 陈昌 查看gridview与baseadapter适配器 继续gr

第二轮冲刺-Runner站立会议03

一.会议细节 时间 2016/5/25 下午 地点 二教 二.会议记录 姓名 今天做了什么 明天要做什么 遇到的问题 李可 查询图标,优化图标 继续查询图标,优化图标 暂无 毛雯雯 查询按钮图标,尝试完善支出界面 将日期和图标尽量添加进去 暂无 张更 主要查看了一下资料,了解了android的activity直接互相传值使用getIntent(),还没有实际编程 尝试使用getIntent()实现查询和日历界面之间日期值得传递 暂无 陈昌 查看gridview与baseadapter适配器 继续

第二轮冲刺-Runner站立会议01

一.会议细节 时间 2016/5/23 地点 学一食堂 记录人 陈昌 二.会议记录 这次会议主要总结前段时间的工作,并对下一阶段做出计划 姓名 今天做了什么 明天要做什么 遇到的问题 李可 主要界面优化图片 明天继续优化 毛雯雯 收集相关数据 找图片   张更 将记计帐各部分基本功能整合,主要实现查询的部分  实现和日期控件的连接,完成个人第一阶段未完成的内容  无法解决日期控件,查询时暂时不能由用户选择日期 陈昌 查看gridview的使用方法 尽量搞懂gridview与baseadapter

三骏码 站立会议05

一.会议细节 时间  2014-4-24  20:30~21:00 地点 学一二楼 记录人  何建勋 二.会议记录 姓名 今天做了什么 明天你要做什么 今天遇到了什么问题 王岸城 无 无 无 苏月  无 无 无  何建勋  学习了与数据库连接 学习sqllite相关知识 无 三.会议内容   要有目标.有针对性的进行学习 四 .会议照片 五 .燃尽图

爆打团队 2016.04.18 站立会议

1. 时间 : 20:00--20:05 2. 人员 : 高鑫 组长 http://www.cnblogs.com/gaolzzxin/ 严一格 http://www.cnblogs.com/yyyyg/ 彭杨 http://www.cnblogs.com/pengy813/ 包玲玲 http://www.cnblogs.com/linglingbao/ 吴军 http://www.cnblogs.com/wujunzero/ 3. 会议内容: 回顾昨天: 周末项目没有进展. 计划今天: 修复快

”耐撕“团队 2016.3.29 站立会议

”耐撕“团队 2016.3.29 站立会议 1. 时间:20:33--21:05 2. 成员: Z 郑蕊 * 组长 (博客:http://www.cnblogs.com/zhengrui0452/), P 濮成林(博客:http://www.cnblogs.com/charliePU/), Q 齐嘉亮(博客:http://www.cnblogs.com/dendroaspis-polylepis/), L  刘伟硕(博客:http://www.cnblogs.com/WeSure6/) 3.会议

站立会议06

遇到的困难: 今早上开站立会议时,大家表示昨天的任务中没遇到什么太大的困难,而且软件大概的雏形已经出现,所以在接下来的几天中我们的重点任务是在重力感应亮.灭屏和各种密码解屏的功能.

会议管理

一.概述 会议的作用不外乎布置工作.总结经验.讨论事项.分析问题.研究对策,或表彰先进.惩戒违章,一言以蔽之就是做出决策或鼓舞士气.管理良好的会议能够集中集体智慧.快速解决问题.迅速推动工作,是企业管理中最常用的方式或手段.企业会议必须有的放矢,不能议而不决,更不能决而不行.会上放空炮,说大话,就会丧失企业决策效力,导致人心涣散,削弱组织活力. 一个企业每年应该召开的会议其实是屈指可数的,固定会议除了企业的年度总结大会.半年总结会.月例会.周例会(班组有晨会或晚会)之外,其余的会议都是专题会议,