\(\mathscr{Description}\)

  Link.

  维护一棵树,初始时树空。接下来 \(n\) 次操作,每次操作加入一片叶子 \(u\),\(u\) 到其邻接点的边权为 \(w\),\(u\) 的半径为 \(r_u\)。每次加入结点后,求出 \(\sum_{u<v}[r_u+r_v\ge\operatorname{dist}(u,v)]\) 的值。强制在线。

  \(n\le10^5\)。

\(\mathscr{Solution}\)

  初学 OI 的时候,第一次听说所谓“超级难写的毒瘤题”就是《紫荆花之恋》,后来每次向尝试也不知道为什么都咕掉了。这几天抽空写了一发却发现……这种码量顶多叫难写,写出来也没啥新奇了。还是挺感慨。(

  显然答案可以随着树的变化维护增量。考虑在一棵静态的树上,对于固定的 \(x\),如何快速求出 \(\sum_{u\neq x}[r_u+r_x\ge\operatorname{dist}(u,x)]\)——要说“维护”这一值,那么可以点分树套平衡树维护。注意本题得用小常数的平衡树(例如替罪羊树)。

  怎么动态维护点分树树形?也可以像替罪羊一样,发现子树不平衡就点分治重构。总复杂度套用替罪羊的证明可知为 \(\mathcal O(n\log^2 n)\)。

\(\mathscr{Code}\)

  巨大常数,代码就自己写嘛 qwq。

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i) typedef long long LL; inline char fgc() {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
EOF : *p++;
} template <typename Tp = int>
inline Tp rint() {
Tp x = 0, s = fgc(), f = 1;
for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
return x * f;
} template <typename Tp>
inline void wint(Tp x) {
if (x < 0) putchar('-'), x = -x;
if (9 < x) wint(x / 10);
putchar(x % 10 ^ '0');
} template <typename Tp>
inline void chkmin(Tp& u, const Tp& v) { v < u && (u = v, 0); }
template <typename Tp>
inline void chkmax(Tp& u, const Tp& v) { u < v && (u = v, 0); }
template <typename Tp>
inline Tp imin(const Tp& u, const Tp& v) { return u < v ? u : v; }
template <typename Tp>
inline Tp imax(const Tp& u, const Tp& v) { return u < v ? v : u; } const int MAXN = 1e5;
const double LUCK = 0.712;
// const double LUCK = 1.;
int n; class ScapegoatTree {
private:
static const int MAXND = 8e6;
int node, ch[MAXND][2], siz[MAXND];
LL val[MAXND];
int cnt[MAXND], sum[MAXND], rcyc[MAXND]; inline int newnd() {
int u = rcyc[0] ? rcyc[rcyc[0]--] : ++node;
ch[u][0] = ch[u][1] = val[u] = 0, cnt[u] = sum[u] = siz[u] = 1;
return u;
} inline void pushup(const int u) {
siz[u] = siz[ch[u][0]] + siz[ch[u][1]] + 1;
sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + cnt[u];
} inline bool valid(const int u) {
return imax(siz[ch[u][0]], siz[ch[u][1]]) <= siz[u] * LUCK + 5;
} inline void collect(const int u, int*& idx) {
if (!u) return ;
collect(ch[u][0], idx), *idx++ = u, collect(ch[u][1], idx);
} inline int rebuild(const int* buc, const int l, const int r) {
if (l > r) return 0;
int mid = l + r >> 1, u = buc[mid];
ch[u][0] = rebuild(buc, l, mid - 1);
ch[u][1] = rebuild(buc, mid + 1, r);
return pushup(u), u;
} inline void balance(int& u) {
if (valid(u)) return ;
static int buc[MAXN + 5], *idx;
collect(u, idx = buc), u = rebuild(buc, 0, idx - buc - 1);
} public:
inline void recycle(const int u) {
if (!u) return ;
rcyc[++rcyc[0]] = u;
recycle(ch[u][0]), recycle(ch[u][1]);
} inline void insert(int& u, const LL x) {
if (!u) return u = newnd(), val[u] = x, void();
balance(u);
if (val[u] == x) return ++cnt[u], ++sum[u], void();
else if (val[u] < x) insert(ch[u][1], x);
else insert(ch[u][0], x);
pushup(u);
} inline int rank(const int rt, const LL x) { // count elements <= x.
int u = rt, ret = 0;
while (u) {
if (val[u] == x) return ret + sum[ch[u][0]] + cnt[u];
else if (val[u] < x) ret += sum[ch[u][0]] + cnt[u], u = ch[u][1];
else u = ch[u][0];
}
return ret;
}
} sct; namespace LCA { const int MAXLG = 16;
int dep[MAXN + 5], fa[MAXN + 5][MAXLG + 2];
LL dis[MAXN + 5]; inline void append(const int u, const int f, const int w) {
fa[u][0] = f, dep[u] = dep[f] + 1, dis[u] = dis[f] + w;
for (int i = 1; fa[u][i - 1]; fa[u][i] = fa[fa[u][i - 1]][i - 1], ++i);
} inline int lca(int u, int v) {
if (dep[u] < dep[v]) u ^= v ^= u ^= v;
per (i, MAXLG, 0) if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
if (u == v) return u;
per (i, MAXLG, 0) if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
} inline LL dist(const int u, const int v) {
return dis[u] + dis[v] - 2 * dis[lca(u, v)];
} } // namespace LCA. namespace DivideTree { int vfa[MAXN + 5], rad[MAXN + 5], ecnt, head[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; inline void link(const int u, const int v) {
graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
} std::vector<int> inc[MAXN + 5];
bool foc[MAXN + 5];
int siz[MAXN + 5], wgt[MAXN + 5], srt[MAXN + 5][2]; inline void collect(const int u, const int fa, std::vector<int>& rec) {
rec.push_back(u);
for (int i = head[u], v; i; i = graph[i].nxt) {
if (foc[v = graph[i].to] && v != fa) {
collect(v, u, rec);
}
}
} inline void findG(const int u, const int fa, const int all, int& rt) {
siz[u] = 1, wgt[u] = 0;
for (int i = head[u], v; i; i = graph[i].nxt) {
if (foc[v = graph[i].to] && v != fa) {
findG(v, u, all, rt), siz[u] += siz[v];
chkmax(wgt[u], siz[v]);
}
}
chkmax(wgt[u], all - siz[u]);
if (!rt || wgt[rt] > wgt[u]) rt = u;
} inline void divide(const int u) {
foc[u] = false;
sct.recycle(srt[u][0]), srt[u][0] = 0;
sct.recycle(srt[u][1]), srt[u][1] = 0;
for (int i = head[u], v; i; i = graph[i].nxt) if (foc[v = graph[i].to]) {
int rt = 0;
std::vector<int> tmp; collect(v, 0, tmp);
findG(v, 0, tmp.size(), rt), inc[rt].swap(tmp);
vfa[rt] = u, divide(rt);
}
} void rebuild(const int); // pre-declare for function `update`. inline void update(int u, const int til, const bool op) {
int pia = 0; LL tmpd = 0;
for (int las = 0, v = u; v != til; v = vfa[las = v]) {
if (op) inc[v].push_back(u);
sct.insert(srt[v][0], tmpd - rad[u]);
if (vfa[v]) {
sct.insert(srt[v][1], (tmpd = LCA::dist(u, vfa[v])) - rad[u]);
}
if (las && inc[las].size() > inc[v].size() * LUCK + 5) pia = v;
}
if (pia) rebuild(pia);
} inline void rebuild(const int u) {
int vf = vfa[u], rt = 0;
for (int v: inc[u]) foc[v] = true;
findG(u, 0, inc[u].size(), rt), inc[rt].swap(inc[u]);
vfa[rt] = vf, divide(rt);
for (int v: inc[rt]) update(v, vfa[rt], 0);
} inline void append(int u, const int rfa, const int r) {
link(u, rfa);
vfa[u] = rfa, rad[u] = r, update(u, 0, 1);
} inline int contri(const int u) {
int ret = sct.rank(srt[u][0], rad[u]) - 1;
for (int las = u, v = vfa[u]; v; v = vfa[las = v]) {
LL d = LCA::dist(u, v);
ret += sct.rank(srt[v][0], rad[u] - d)
- sct.rank(srt[las][1], rad[u] - d);
}
return ret;
} } // namespace DivideTree. int main() {
rint(), n = rint();
LL ans = 0;
rep (i, 1, n) {
int a = rint() ^ (ans % 1'000'000'000), c = rint(), r = rint();
LCA::append(i, a, c);
DivideTree::append(i, a, r);
ans += DivideTree::contri(i);
wint(ans), putchar('\n');
}
return 0;
}

Solution -「WC 2014」「洛谷 P3920」紫荆花之恋的更多相关文章

  1. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  2. Solution -「JSOI 2019」「洛谷 P5334」节日庆典

    \(\mathscr{Description}\)   Link.   给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的).   \(|S|\le3\time ...

  3. Solution -「洛谷 P4372」Out of Sorts P

    \(\mathcal{Description}\)   OurOJ & 洛谷 P4372(几乎一致)   设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...

  4. Solution -「POI 2010」「洛谷 P3511」MOS-Bridges

    \(\mathcal{Description}\)   Link.(洛谷上这翻译真的一言难尽呐.   给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...

  5. Solution -「APIO 2016」「洛谷 P3643」划艇

    \(\mathcal{Description}\)   Link & 双倍经验.   给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...

  6. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  7. 「洛谷3338」「ZJOI2014」力【FFT】

    题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...

  8. 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】

    题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...

  9. 「洛谷3870」「TJOI2009」开关【线段树】

    题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...

  10. 「洛谷5300」「GXOI/GZOI2019」与或和【单调栈+二进制转化】

    题目链接 [洛谷传送门] 题解 按位处理. 把每一位对应的图都处理出来 然后单调栈处理一下就好了. \(and\)操作处理全\(1\). \(or\)操作处理全\(0\). 代码 #include & ...

随机推荐

  1. ESP8266 + MQTT (platformio 开发环境)加用户名和密码

    ESP8266 + MQTT git 地址: https://gitee.com/zhudachangs/esp8266-mqtt.git (如果无法打开说明在审核) 引用库 include < ...

  2. python实现的扫雷游戏的AI解法(启发式算法)

    相关: python编写的扫雷游戏 如何使用计算机程序求解扫雷游戏 本文中实现的<扫雷>游戏的AI解法的项目地址: https://openi.pcl.ac.cn/devilmaycry8 ...

  3. 能不能用uni开发一个线上运动会的APP、小程序?

    引言:uni-app凭借其强大的跨平台能力,成为开发AI运动类APP和小程序的首选框架.本文旨在探讨基于uni进行开发AI运动小程序.APP开发,以及开发过程中遇到的技术难点,并为您介绍一个开箱即用的 ...

  4. 编程辅助工具之Kite

    python作为一门门槛很低但又功能强大的编程语言,现在已经得到了非常广泛的使用,但是它的常用库非常多,而且往往更新后许多方法都有所变化,因此想要记住其大部分函数的用法对于大部分人来说比较困难,因而会 ...

  5. Blazor 组件库 BootstrapBlazor 中AutoComplete组件介绍

    AutoComplete组件介绍 AutoComplete组件和文本框基本上样子是一样的,只不过AutoComplete组件还带有一个下拉列表,可以从中选择对应的内容. 其同样继承自Bootstrap ...

  6. Codeforces Round 832 (Div2)

    Swap Game Alice 和 Bob 两个人在玩游戏. 有一个长度为 \(n\) 的序列 \(a\),Alice 和 Bob 两人轮流完成一个操作,Alice 先开始. 每个人可以将数列的第一个 ...

  7. MySQL底层概述—9.ACID与事务

    大纲 1.ACID之原子性 2.ACID之持久性 3.ACID之隔离性 4.ACID之一致性 5.ACID的关系 6.事务控制演进之排队 7.事务控制演进之排它锁 8.事务控制演进之读写锁 9.事务控 ...

  8. javascript的一些API接口的使用

    1.blob http URL 在编辑器中,有的情况下插入图片,会讲图片转成 blob:http://localhost/*** 的这种形式.这种形式的URL实际数据是存放在浏览器的内存中. 这种情况 ...

  9. OSG开发笔记(三十七):OSG基于windows平台msvc2017x64编译器官方稳定版本OSG3.4.1搭建环境并移植Demo

    前言   自行编译的osg版本插件比较多,如果对版本没有特定要求,但是对环境编译器有特定要求,可以反向融合编译器符合要求的osg版本.   OSG下载过程   osg官网:http://www.osg ...

  10. .NET Core 锁(Lock)底层原理浅谈

    CPU原子操作 原子操作,指一段逻辑要么全部成功,要么全部失败.概念上类似数据库事物(Transaction). CPU能够保证单条汇编的原子性,但不保证多条汇编的原子性 那么在这种情况下,那么CPU ...