UOJ#55 [WC2014]紫荆花之恋
题目描述
强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。
仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点,每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 \(i\) 都有一个感受能力值 \(r_i\),小精灵 \(i, j\) 成为朋友当且仅当在树上 \(i\) 和 \(j\) 的距离 \(\text{dist}(i, j) \leq r_i + r_j\),其中 \(\text{dist}(i, j)\) 表示在这个树上从 \(i\) 到 \(j\) 的唯一路径上所有边的边权和。
强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。
我们假定这个树一开始为空,节点按照加入的顺序从 \(1\) 开始编号。由于强强非常好奇,你必须在他每次出现新结点后马上给出总共的朋友对数,不能拖延哦。
输入格式
第一行包含一个整数,表示测试点编号。
第二行包含一个正整数 \(n\),表示总共要加入的节点数。
我们令加入节点前的总共朋友对数是 \(\text{last_ans}\),在一开始时它的值为 \(0\)。
接下来 \(n\) 行中第 \(i\) 行有三个非负整数 \(a_i, c_i, r_i\),表示结点 \(i\) 的父节点的编号为 \(a_i \oplus (\text{last_ans} \bmod 10^9)\)(其中 \(\oplus\) 表示异或,\(\bmod\)表示取余,数据保证这样操作后得到的结果介于 \(1\) 到 \(i - 1\) 之间),与父结点之间的边权为 \(c_i\),节点 \(i\) 上小精灵的感受能力值为 \(r_i\)。
注意 \(a_1 = c_1 = 0\),表示 \(1\) 号节点是根结点,对于 \(i > 1\),父节点的编号至少为 \(1\)。
输出格式
包含 \(n\) 行,每行输出 \(1\) 个整数,表示加入第 \(i\) 个点之后,树上有几对朋友。
样例一
input
0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4
output
0
1
2
4
7
样例二
见样例数据下载。
限制与约定
对于所有数据,满足 \(1 \leq c_i \leq 10000\),\(a_i \leq 2 \times 10^9\),\(r_i \leq 10^9\),\(n \leq 100000\)
此题 hack 时忽略输入数据中给定的测试点编号对测试点的限制。
祝大家一遍 AC,求不虐萌萌哒测评机!
时间限制:\(12\texttt{s}\)
空间限制:\(512\texttt{MB}\)
题解
首先,我们发现实际上每次只需要统计新加进的结点有多少合法解。那么,我们将\(dist(i, j)\)写成\(dist(i,p)+dist(j,p)\),其中\(p\)是\(i\)和\(j\)的LCA。
那么,
\Leftrightarrow dist(i,p)+dist(j,p)&\leq r_i+r_j\\
\Leftrightarrow r_i-dist(i,p)&\geq dist(j,p)-r_j\end{aligned}\]
所以,我们枚举新加的结点的所有祖先\(p\),计算以\(p\)为LCA的满足条件的点对\((i,j)\)有多少个即可。
计算时,先查询\(p\)子树在添加\(i\)之前有多少\(j\)满足\(r_i-dist(i,p)\geq dist(j,p)-r_j\),再减去其中LCA不是\(p\)的(即,\(i\)和\(j\)在\(p\)的同一棵子树里,这样我们就要查询在这棵子树里有多少\(j\)满足此条件)。
然而,要高效地维护这个信息,就需要在每个结点上维护一个Treap(记录所有的\(dist(j,p)-r_j\)),但这样当树退化为链,单次加入时间复杂度增加为\(O(nlogn)\),空间复杂度增加为\(O(n^2)\)。
于是,借鉴替罪羊树的思想,我们在某个结点子树内的点个数大于其父亲子树内点的个数的\(\alpha\in(0,1)\)倍的时候暴力重构其父亲的子树。既然要求重构之后尽量平衡,理所当然地选用点分治。
这样,上面的分析中所有的“父亲”和“祖先”都应理解为点分治树上的祖先(LCA当然也是),但是答案仍是正确的,因为我们仍然不重不漏地枚举了所有可能的点。
代码实现上,还要写一个倍增LCA以查询\(dist\),因为点分治之后的祖先不一定是原树上的祖先,不能直接用\(dep\)相减。
哦,还有,要写垃圾回收,因为有重构treap,之前的内存必须要重复利用。
代码
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <set>
typedef long long LL;
const int N = 100050;
int pre[N], to[N * 2], nxt[N * 2], c[N * 2], cnt = 0;
int r[N];
inline void add_edge(int x, int y, int v) {
nxt[cnt] = pre[x];
c[cnt] = v;
to[pre[x] = cnt++] = y;
nxt[cnt] = pre[y];
c[cnt] = v;
to[pre[y] = cnt++] = x;
}
namespace Tree{
int dep[N], dep2[N], fa[N][17];
void insert(int x, int c, int f) {
add_edge(x, f, c);
dep[x] = dep[fa[x][0] = f] + 1;
dep2[x] = dep2[f] + c;
for (int i = 1; i < 17; ++i)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
int LCA(int x, int y) {
if (dep[x] > dep[y]) std::swap(x, y);
for (int i = 16; ~i; --i)
if (dep[fa[y][i]] >= dep[x])
y = fa[y][i];
for (int i = 16; ~i; --i)
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
return x == y ? x : fa[x][0];
}
int dis(int x, int y) {
int l = LCA(x, y);
return dep2[x] + dep2[y] - 2 * dep2[l];
}
};
struct Treap;
typedef Treap* PTreap;
struct Treap{
static std::stack<PTreap> bin;
PTreap lch, rch;
int val, key, cnt, siz;
void* operator new(size_t, int v) {
Treap *res;
res = bin.top();
bin.pop();
res->val = v; res->key = rand();
res->cnt = 1; res->siz = 1;
res->lch = res->rch = NULL;
return res;
}
void operator delete(void *t) {
bin.push((PTreap)t);
}
void update() {
siz = cnt;
if (lch != NULL) siz += lch->siz;
if (rch != NULL) siz += rch->siz;
}
friend void Zig(PTreap &t) { //右旋
PTreap l = t->lch;
t->lch = l->rch;
l->rch = t;
t->update();
l->update();
t = l;
}
friend void Zag(PTreap &t) { //左旋
Treap *r = t->rch;
t->rch = r->lch;
r->lch = t;
t->update();
r->update();
t = r;
}
friend int query(PTreap o, int x) {
if (o == NULL) return 0;
if (o->val > x) return query(o->lch, x);
else return query(o->rch, x) + (o->lch == NULL ? 0 : o->lch->siz) + o->cnt;
}
friend void insert(PTreap &o, int x) {
if (o == NULL)
o = new (x)Treap;
else if (o->val == x)
++o->cnt;
else if (o->val > x) {
insert(o->lch, x);
if (o->lch->key > o->key)
Zig(o);
} else {
insert(o->rch, x);
if (o->rch->key > o->key)
Zag(o);
}
o->update();
}
friend void remove(PTreap &x) {
if (x == NULL) return;
remove(x->lch);
remove(x->rch);
delete x; x = NULL;
}
};
std::stack<PTreap> Treap::bin;
namespace Dynamic_TreeDivision{
PTreap tree[N], sonTree[N];
int time, vise[N * 2];
int fa[N], vis[N];
std::set<int> son[N];
void remove(int x) {
vis[x] = time;
for (std::set<int>::iterator i = son[x].begin(); i != son[x].end(); ++i) {
remove(*i);
remove(sonTree[*i]);
}
son[x].clear();
remove(tree[x]);
}
int getCentre(int x, int f, int siz, int &ct) {
int res = 1;
bool ok = true;
for (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (to[i] == f) continue;
if (vis[to[i]] != time) continue;
int ss = getCentre(to[i], x, siz, ct);
if (ss > siz / 2) ok = false;
res += ss;
}
if (siz - res > siz / 2) ok = false;
if (ok) ct = x;
return res;
}
void insertAll(int x, int f, int dep, PTreap &p) {
insert(p, dep - r[x]);
for (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (to[i] == f) continue;
if (vis[to[i]] != time) continue;
insertAll(to[i], x, dep + c[i], p);
}
}
int divide(int x) {
getCentre(x, 0, getCentre(x, 0, 1000000000, x), x);
insertAll(x, 0, 0, tree[x]);
for (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (vis[to[i]] != time) continue;
vise[i] = vise[i ^ 1] = time;
PTreap p = NULL;
insertAll(to[i], 0, c[i], p);
int s = divide(to[i]);
fa[s] = x;
son[x].insert(s);
sonTree[s] = p;
}
return x;
}
void rebuild(int x) {
++time;
remove(x);
int ff = fa[x];
PTreap p = sonTree[x];
sonTree[x] = NULL;
if (ff != 0) son[ff].erase(x);
x = divide(x);
fa[x] = ff;
sonTree[x] = p;
if (ff != 0) son[ff].insert(x);
}
LL insert(int x, int f) {
LL ans = 0;
son[f].insert(x);
fa[x] = f;
for (int i = x; i; i = fa[i]) {
if (fa[i] != 0) {
int d = Tree::dis(fa[i], x);
ans += query(tree[fa[i]], r[x] - d);
ans -= query(sonTree[i], r[x] - d);
insert(sonTree[i], d - r[x]);
}
int d = Tree::dis(i, x);
insert(tree[i], d - r[x]);
}
int rebuildx = 0;
for (int i = x; fa[i]; i = fa[i])
if (tree[i]->siz > tree[fa[i]]->siz * 0.88)
rebuildx = fa[i];
if (rebuildx) rebuild(rebuildx);
return ans;
}
};
Treap node[N * 100];
int main() {
for (int i = 0; i < N * 100; ++i)
Treap::bin.push(node + i);
int n, a, cc, v;
scanf("%*d%d", &n);
LL lastans = 0;
pre[0] = -1;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &a, &cc, &v);
r[i] = v;
a ^= lastans % 1000000000;
pre[i] = -1;
Tree::insert(i, cc, a);
lastans += Dynamic_TreeDivision::insert(i, a);
printf("%lld\n", lastans);
}
return 0;
}
UOJ#55 [WC2014]紫荆花之恋的更多相关文章
- bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400
3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec Memory Limit: 512 MBSubmit: 159 Solved: 40[Submit][Status] ...
- 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT
[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...
- BZOJ 3435: [Wc2014]紫荆花之恋
二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...
- luogu P3920 [WC2014]紫荆花之恋
LINK:紫荆花之恋 每次动态加入一个节点 统计 有多少个节点和当前节点的距离小于他们的权值和. 显然我们不能n^2暴力. 考虑一个简化版的问题 树已经给出 每次求某个节点和其他节点的贡献. 不难想到 ...
- BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋
https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...
- BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- [WC2014]紫荆花之恋(动态点分治+替罪羊思想)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- 【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)
Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...
随机推荐
- Python Socket 编程示例 Echo Server
简评:我们已经从「Python Socket 编程概览」了解了 socket API 的概述以及客户端和服务器的通信方式,接下来让我们创建第一个客户端和服务器,我们将从一个简单的实现开始,服务器将简单 ...
- QuantLib 金融计算——基本组件之 Calendar 类
目录 QuantLib 金融计算--基本组件之 Calendar 类 Calendar 对象的构造 一些常用的成员函数 自定义假期列表 工作日修正 如果未做特别说明,文中的程序都是 Python3 代 ...
- Hibernate 工具类
1.HibernateConfigUtil.java(HIbernate配置工具类) import org.hibernate.Session; import org.hibernate.Sessio ...
- mfix中输出DEM颗粒的固相速度到网格
基于mfix-19.1.2版本 方法一:直接输出差值网格固相速度 注:这种方式只适用于garg 2012颗粒差值格式在DEM中,默认是无法输出固相速度的网格值的: 但是通过搜索des文件夹下V_s关键 ...
- P3440 [POI2006]SZK-Schools
传送门 应该是很显然的费用流模型吧... $S$ 向所有学校连边,流量为 $1$,费用为 $0$(表示每个学校要选一个编号) 学校向范围内的数字连边,流量为 $1$,费用为 $c|m-m'|$(表示学 ...
- 图解http 笔记
一,了解web以及网络基础 1,使用http协议访问web web页面是由web浏览器根据地址栏中指定的url从web服务器获取文件资源等信息然后显示的页面. 像这种通过发送请求获取服务器资源的web ...
- MAVEN打zip包
https://blog.csdn.net/yulin_hu/article/details/81835945 https://www.cnblogs.com/f-zhao/p/6929814.htm ...
- Mac 10.12安装Windows远程桌面工具Microsoft Remote Desktop
说明:之前Office自带的Windows远程桌面工具虽然简便,但是保存的服务器列表有限.而这个微软推出的自家工具可以完美解决这些问题. 下载: (链接:https://pan.baidu.com/s ...
- [Xarmrin.IOS]使用Build Host 在Windows上建置IOS程式及DeBug (转帖)
使用Xamarin開發IOS程式時, 必須要在Mac上才可以編譯程式,若想在windows系統上編譯,則可透過Build host的方式, 但還是要有一台Mac的電腦就是了XD 首先你的Mac必須要已 ...
- 删除数据库的数据后让id从1开始算
delete from t_AttendanceRecorddbcc checkident('t_AttendanceRecord',reseed,0) truncate table 表名称