题意

​略​

分析

引用PoPoQQQ的话

吾辈有生之年终于把这道题切了。。。QAQ

(蒟蒻狂笑)

Orz PoPoQQQ,我又抄PoPoQQQ的题解了

  • 突然发现有旋Treap没那么难写
  • 学习了一波C++语法,第一次用指针,什么
  • BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_子树

  • 也大概会用了…(这玩意还能重载…跪了)
  • 学习了一波点分治的正确写法.我之前写的都是什么烂玩意儿
  • dalao代码的细节处理得好啊,学习了学习了 (这就是你抄代码的原因?)

这种符合某条件的点对,首先就想到点分治…先假设树的形态是固定的,我们考虑满足

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_02

的点对,假设它们在点分树上的

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_03


BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_04

,定义

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_子树_05


BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_06


BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_04

的距离.那么有

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_08

那么我们将子树中所有

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_09

插入平衡树,只需要查询

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_子树_10

在平衡树中的排名就能查询和

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_11

组成点对的

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_06

的数量.

由于树的形态不确定,那么就动态点分治就行了.那么我们像点分治常见的套路,在点分树上的每一个点维护两颗平衡树,一棵

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_13

表示子树内的所有点

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_09

组成的平衡树,一棵

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_15

维护这一棵子树对父亲的贡献.那么查询一个点的时候,从那个点往上计算,每一次用父亲的

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_13

所查询的值减去这棵子树的

BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_平衡树_15

所查询的值 就能不重不漏地算出答案(因为要去除点对在同一棵子树内的情况).然后修改可以和查询在同一个函数内进行,先查后修改.

由于动态点分治可能会出现一条链的情况,那么要像替罪羊树的思想,子树不平衡就重构…


BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋  (替罪羊树 动态点分治 套 Treap)_#include_18

存边挺省事的,就是比较慢…

CODE

手写rand真快 … 要更详细的代码注释去PoPoQQQ看

#include <set>
#include <queue>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
char ch; int flg = 1; for(;!isdigit(ch=getchar());)if(ch=='-')flg=-flg;
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); res*=flg;
}
typedef long long LL;
const int MAXN = 100100;
const int mod = 1e9;

int n, r[MAXN];
LL lastans;

int Rand() { //手写rand(qwq)
static int G = 3;
return G = 3ll * G % 998244353;
}

namespace Gragh {

struct edge { int to, nxt, len, ban; }e[MAXN<<1];
int fir[MAXN], cnt = 1, f[MAXN][17], dep[MAXN], dis[MAXN];

inline void Add(int u, int v, int w) {
e[++cnt] = (edge) { v, fir[u], w, -1 }, fir[u] = cnt;
}

inline void Build_LCA(int i) {
for(int j = 1; j < 17; ++j)
f[i][j] = f[f[i][j-1]][j-1];
}

inline int LCA(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int i = 16; ~i; --i)
if((dep[u]-dep[v])&(1<<i)) u = f[u][i];
if(u == v) return u;
for(int i = 16; ~i; --i)
if(f[u][i] != f[v][i])
u = f[u][i], v = f[v][i];
return f[u][0];
}

inline int Dist(int u, int v) {
return dis[u] + dis[v] - (dis[LCA(u, v)]<<1);
}

}

struct Treap {

static queue<Treap*> bin;

Treap *ls, *rs;
int val, key, cnt, size;

inline void* operator new (size_t, int _) {
Treap *re;
if(bin.size()) re = bin.front(), bin.pop();
else {
static Treap *mempool, *C; //static相当于表示全局变量
if(C == mempool) mempool = (C = new Treap[1<<16])+(1<<16); //内存不够就又开多点
re = C++;
}
re->ls = re->rs = 0x0;
re->val = _;
re->key = Rand();
re->cnt = re->size = 1;
return re;
}

inline void operator delete (void *p) {
bin.push((Treap*)p); //重载delete 回收利用
}

inline void Push_Up() {
size = cnt;
if(ls) size += ls->size;
if(rs) size += rs->size;
}

inline friend void Zig(Treap *&x) {
Treap *y = x->ls;
x->ls = y->rs;
y->rs = x; x = y;
x->rs->Push_Up();
x->Push_Up();
}

inline friend void Zag(Treap *&x) {
Treap *y = x->rs;
x->rs = y->ls;
y->ls = x; x = y;
x->ls->Push_Up();
x->Push_Up();
}

friend void Insert(Treap *&x, int y) { //插入进Treap
if(!x) { x = new(y) Treap; return; }
if(y == x->val) x->cnt++;
else if(y < x->val) {
Insert(x->ls, y);
if(x->ls->key > x->key)
Zig(x);
}
else {
Insert(x->rs, y);
if(x->rs->key > x->key)
Zag(x);
}
x->Push_Up();
}

friend void Delete(Treap *&x) { //删除以x为根的Treap子树
if(!x) return;
Delete(x->ls);
Delete(x->rs);
delete x; x = 0x0;
}

friend int Query(Treap *x, int y) {
if(!x) return 0;
if(y < x->val) return Query(x->ls, y);
else return (x->ls ? x->ls->size : 0) + x->cnt + Query(x->rs, y);
}

};

queue<Treap*> Treap::bin; //声明一下才能用

namespace Dynamic_TDC {

using namespace Gragh; //在namespace里using别的namespace...
#define alpha 0.88
#define Sit set<int>::iterator

int fa[MAXN], v[MAXN], cur;

Treap *t[MAXN], *tf[MAXN];

set<int> to[MAXN];

void Del(int x) { //删除x为根的子树
v[x] = cur;
for(Sit it = to[x].begin(); it != to[x].end(); ++it)
Del(*it), Delete(tf[*it]);
to[x].clear();
Delete(t[x]);
}

int Get_Size(int x, int ff) { //求SIZE
int re = 1;
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff)
re += Get_Size(e[i].to, x);
return re;
}

int Get_G(int x, int ff, int Size, int &cg) { //求重心
int re = 1; bool flag = 1;
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff) {
int temp = Get_G(e[i].to, x, Size, cg);
if(temp<<1 > Size) flag = 0;
re += temp;
}
if((Size-re)<<1 > Size) flag = 0;
if(flag) cg = x; return re;
}

void DFS(int x, int ff, int dpt, Treap *&p) { //将子树内的点全部插入Treap
Insert(p, dpt-r[x]);
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff)
DFS(e[i].to, x, dpt+e[i].len, p);
}

int TDC(int x) { //点分治
int Size = Get_Size(x, 0);
Get_G(x, 0, Size, x);
DFS(x, 0, 0, t[x]);
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur) {
Treap *p = 0x0;
DFS(e[i].to, x, e[i].len, p); //统计子树对父亲x的贡献
e[i].ban = e[i^1].ban = cur; //打上不能再访问的标记
int temp = TDC(e[i].to);
tf[temp] = p; fa[temp] = x; to[x].insert(temp);
}
return x;
}

inline void Re_build(int x) { //重建
++cur; Del(x); int y = fa[x];
Treap *p = tf[x]; tf[x] = 0x0;
int temp = TDC(x);
fa[temp] = y;
if(y) to[y].erase(x), to[y].insert(temp); //先删边再加边
tf[temp] = p;
}

inline void Insert(int x) { //插入点分树
for(int i = x; i; i = fa[i]) {
if(fa[i]) {
int d = Dist(x, fa[i]);
lastans += Query(t[fa[i]], r[x]-d);
lastans -= Query(tf[i], r[x]-d);
Insert(tf[i], d-r[x]);
}
int D = Dist(x, i);
Insert(t[i], D-r[x]);
}

int temp = 0;//替罪咩重建
for(int i = x; fa[i]; i = fa[i])
if((double)t[i]->size / t[fa[i]]->size > alpha)
temp = fa[i];
if(temp) Re_build(temp);
}

}

int main () {
read(n), read(n);
int x, y;
for(int i = 1; i <= n; ++i) {
read(x), read(y), read(r[i]);
x ^= (lastans % mod);
Gragh::Add(i, x, y);
Gragh::Add(x, i, y);
Gragh::f[i][0] = x;
Gragh::dep[i] = Gragh::dep[x] + 1;
Gragh::dis[i] = Gragh::dis[x] + y;
Gragh::Build_LCA(i); //预处理倍增

Dynamic_TDC::to[x].insert(i);
Dynamic_TDC::fa[i] = x;
Dynamic_TDC::Insert(i);

printf("%lld\n", lastans);
}
}

看着250行抄来的代码陷入沉思