[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】
题目分析
使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目。
对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点。
线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和。
在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -x, 由白变黑就 +x)。
在处理完所有操作后,我们进行一次 DFS,自底向上将线段树进行合并。
目前 DFS(x),先递归处理完 x 的每棵子树,然后枚举 x 的每棵子树,依次将它们的线段树合并到 x 的线段树上。
现在已经将 x 的前 j-1 棵子树的线段树合并到了 x 的线段树上,现在将第 j 棵子树的线段树合并到 x 的线段树上。
对于处于 j 子树内的 a 和处于 x 点或前 j-1 棵子树内的黑点,它们的 LCA 就是 x 点,因此他们对 x 的权值有贡献。
同理,处于 j 子树内的黑点和处于 x 点或前 j-1 棵子树内的 a ,他们的 LCA 也是 x 点,也要计算他们对 x 的权值的贡献。
一个黑点权值修改会对时间 key 比它大的 1 操作产生影响。
合并时,记合并的两棵线段子树为 (x, y),那么答案就要加上 Son[x][0] 的黑点权值修改 * Son[y][1] 的 1 操作个数。
同理,答案也要加上 Son[y][0] 的黑点权值修改 * Son[x][1] 的 1 操作个数。
然后递归下去合并 (Son[x][0], Son[y][0]) ,合并 (Son[x][1], Son[y][1]),继续计算两边子树内部的答案。
同时注意,这样计算的答案不包括 a 点本身就是一个黑点时贡献的权值,所以要单独加上这个情况的权值。
代码
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm> using namespace std; typedef long long LL; inline void Read(int &Num)
{
char c = getchar();
bool Neg = false;
while (c < '0' || c > '9')
{
if (c == '-') Neg = true;
c = getchar();
}
Num = c - '0'; c = getchar();
while (c >= '0' && c <= '9')
{
Num = Num * 10 + c - '0';
c = getchar();
}
if (Neg) Num *= -1;
} const int MaxN = 200000 + 5, MaxNode = 7200000 + 5; int n, m, Index;
int A[MaxN], Root[MaxN], Son[MaxNode][2], T[MaxNode]; LL Cnt;
LL Ans[MaxN], Sum[MaxNode]; struct Edge
{
int v;
Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y)
{
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} inline void Update(int x)
{
T[x] = T[Son[x][0]] + T[Son[x][1]];
Sum[x] = Sum[Son[x][0]] + Sum[Son[x][1]];
} void Add(int &x, int s, int t, int Pos, int Ds, int Dt)
{
if (x == 0) x = ++Index;
if (s == t)
{
Sum[x] += (LL)Ds;
T[x] += Dt;
return;
}
int m = (s + t) >> 1;
if (Pos <= m) Add(Son[x][0], s, m, Pos, Ds, Dt);
else Add(Son[x][1], m + 1, t, Pos, Ds, Dt);
Update(x);
} int Merge(int x, int y, int s, int t)
{
if (!x) return y;
if (!y) return x;
if (s == t)
{
T[x] += T[y];
Sum[x] += Sum[y];
return x;
}
Cnt += (LL)T[Son[x][1]] * Sum[Son[y][0]];
Cnt += (LL)T[Son[y][1]] * Sum[Son[x][0]];
int m = (s + t) >> 1;
Son[x][0] = Merge(Son[x][0], Son[y][0], s, m);
Son[x][1] = Merge(Son[x][1], Son[y][1], m + 1, t);
Update(x);
return x;
} void Solve(int x, int Fa)
{
for (Edge *j = Point[x]; j; j = j -> Next)
{
if (j -> v == Fa) continue;
Solve(j -> v, x);
}
for (Edge *j = Point[x]; j; j = j -> Next)
{
if (j -> v == Fa) continue;
Cnt = 0;
Root[x] = Merge(Root[x], Root[j -> v], 0, m);
Ans[x] += Cnt;
}
} int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
Read(A[i]);
if (A[i] != 1) A[i] = 0;
}
int a, b;
for (int i = 1; i < n; ++i)
{
Read(a); Read(b);
AddEdge(a, b); AddEdge(b, a);
}
for (int i = 1; i <= n; ++i)
Add(Root[i], 0, m, 0, A[i] * i, 0);
int f, x;
for (int i = 1; i <= m; ++i)
{
Read(f); Read(x);
if (f == 1)
{
Add(Root[x], 0, m, i, 0, 1);
if (A[x]) Ans[x] += (LL)x;
}
else
{
A[x] ^= 1;
if (A[x]) Add(Root[x], 0, m, i, x, 0);
else Add(Root[x], 0, m, i, -x, 0);
}
}
Solve(1, 0);
for (int i = 1; i <= n; ++i)
printf("%lld\n", Ans[i]);
return 0;
}
[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】的更多相关文章
- [XJOI NOI2015模拟题13] B 最小公倍数 【找规律】
题目链接:XJOI - NOI2015-13 - B 题目分析 通过神奇的观察+打表+猜测,有以下规律和性质: 1) 删除的 n 个数就是 1~n. 2) 当 c = 2 时,如果 n + 1 是偶数 ...
- [XJOI NOI2015模拟题13] A 神奇的矩阵 【分块】
题目链接:XJOI NOI2015-13 A 题目分析 首先,题目定义的这种矩阵有一个神奇的性质,第 4 行与第 2 行相同,于是第 5 行也就与第 3 行相同,后面的也是一样. 因此矩阵可以看做只有 ...
- Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)
题目链接 2016 ACM-ICPC EC-Final Problem G 题意 给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过$w$的 ...
- [CSP-S模拟测试]:影魔(树状数组+线段树合并)
题目背景 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己 ...
- 学军NOI训练13 T3 白黑树
唉,大学军有自己的OJ就是好,无限orz 只有周六的比赛是开放的囧,这场比赛最后因为虚拟机卡住没有及时提交…… 否则就能让大家看到我有多弱了…… 前两题题解写的很详细,可以自己去看,我来随便扯扯T3好 ...
- 【2018.06.26NOIP模拟】T1纪念碑square 【线段树】*
[2018.06.26NOIP模拟]T1纪念碑square 题目描述 2034年,纪念中学决定修建校庆100周年纪念碑,作为杰出校友的你被找了过来,帮校方确定纪念碑的选址. 纪念中学的土地可以看作是一 ...
- ZOJ 4100 浙江省第16届大学生程序设计竞赛 A题 Vertices in the Pocket 线段树+并查集
正赛的时候完全没看这个题,事后winterzz告诉我他想出来的解法. 首先题意是给出n个点,m次操作. 操作有一种是连接两个点,另一种是求此时再为这个图连k条边,最少和最多能有几个联通块. 最少的求法 ...
- 【BZOJ】3038: 上帝造题的七分钟2(线段树+暴力)
http://www.lydsy.com:808/JudgeOnline/problem.php?id=3038 这题我就有得吐槽了,先是线段树更新写错,然后不知哪没pushup导致te,精度问题sq ...
- codevs2492上帝造题的七分钟 2(线段树)
/* 区间修改 区间查询 可以用线段树搞 但是一般的标记下放对这个题好像不合适 只能改叶子 然后更新父亲(虽然跑的有点慢) 小优化:如果某个点是1 就不用再开方了 所以搞一个f[i]标记 i 这个点还 ...
随机推荐
- Makefile的规则
在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则:最基本的编写规则的方法是从最终的源程序文件一个一个的查看源码文件.把它们要生成的目标文件作为目标,而C语言源码文件和源 ...
- 解决闪光灯代码在Nexus5上面打不开的问题
参考:http://blog.csdn.net/cy524563/article/details/41545387 关键在于: int textureId = 0;whyCamera.setPrevi ...
- SQL语句打印四个方向的9 9 乘法表
declare @i int ,@j int ,@s nvarchar(max) set @i = 1 while @i <=9 begin set @s = ' ' set @j = 1 wh ...
- 20160503-spring入门1
一.Spring是什么 Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目得是简化企业开发. IOC 控制反转 publ ...
- 20160502-struts2入门--ognl表达式
一.OGNL表达式语言 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目. Struts 2框架使用OGNL作为默认的表达式语言 ...
- Extjs搜索域使用
要在使用的panel在预先加载搜索域类requires : ["Ext.ux.form.SearchField"],
- XCode实用好用插件集锦
工欲善其事,必先利其器,iOS开发运用插件可以大大提高开发的效率. 原文地址: http://www.oschina.net/project/tag/432/xcode-plugins
- 让C# Excel导入导出,支持不同版本的Office
问题:最近在项目中遇到,不同客户机安装不同Office版本,在导出Excel时,发生错误. 找不到Excel Com组件,错误信息如下. 未能加载文件或程序集“Microsoft.Office.Int ...
- 【干货分享】.NET单元测试电子书
文章目录: 引言 书籍首页 书籍目录 备注 下载 回到顶部 引言: 平时没有项目压力时候,首先想到的是充电学习,要系统学习都会想到购买书籍,或者向同事借阅.买的书多了烦恼也来了,因为大学到现在5 ...
- VirtualBox single usermode boot
VirtualBox single usermode boot 当系统因为某些原因无法通过图形界面登录VirtualBox内的系统时,可以通过Grub进入命令行模式/单一用户界面模式. 参考: 1.R ...