关于求答案

对于每一个分治中心 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define,求经过它的点对 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_02 的贡献
需要 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_03
也就是 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define_04
可以用平衡树维护一波 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_05,然后就是查询比一个数小的个数
但这样是算重了的,因为 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_06 可能在 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define 下方的同一棵子树
然后还需要维护一棵减去贡献的平衡树
不用想复杂,减去的贡献 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_02 当且仅当 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_02 在切掉 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define 的情况下还在一个联通块
于是可以对这些连通块维护平衡树存同样的值
也就是说当前点 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_11 的贡献,就是在每一个祖先 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define 查一个答案,然后在 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define 割掉后 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_11 的联通块的平衡树中查一个答案减掉


为了避免点分树每次跳 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define_15 的繁琐讨论
我们每个点开一个 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_17
表示这个点的祖先,到祖先的距离以及切掉祖先后 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_11 所在的平衡树的编号


因为要插叶子,所以点分树会不平衡,类似替罪羊的思想,设一个平衡因子 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define_19,定期重构


重构点分树
首先需要知道支持这个重构操作需要维护什么?
子树需要清空,每个点开 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16 暴力维护子树中的结点
然后对于一个子孙的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_17[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_22 我们需要把它清空,所以一个点要维护切掉它过后的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_22 的集合,同样 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16 暴力维护
需要清空的东西:
1.子树所有点的平衡树
2.子树所有点维护儿子的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16
3.子树用来减的平衡树
4.子树所有维护祖先的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16
5.维护割掉它剩下的联通块的平衡树

细节:发现祖先的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16 是按顺序插的,深的在下面,于是弹一个点的祖先的时候可以暴力弹的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_28


点分治
跟一般的点分治一样,[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_29 的时候插一下维护祖先的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16
然后暴力把 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_29 到的点插到自己的儿子集合,把 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define_32 存下来平衡树 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_33 一波
然后还要维护一圈的平衡树,把那一棵子树的点弄出来暴力 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_33 就可以了


插入
[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_28 挂在 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_11
先把 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_11 维护祖先的 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16 复制给 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_28,然后将里面的每一个 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_40 加上 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_41
在祖先 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_#define 的平衡树中插入 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_43 的值
然后需要在 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_28[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_16 中插入 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_46 这个三元组
然后暴力跳祖先,把 [WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_28 加到它们的集合
中途遇到不平衡就找到最高的并重构


总结
再次理清思路:

  1. 查询答案需要维护平衡树,同时需要容斥掉在一个子树不合法的情况,所以需要维护一个点割掉它剩下的一圈平衡树
  2. 一个点要查答案需要知道所有祖先,祖先的平衡树,祖先割掉后[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_子树_48属于的那个连通块的平衡树,用一个[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_49
  3. 一个点要重构整个子树需要知道子树的所有点,[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)_平衡树_49暴力维护,插入时更新

主要细节就是这些,第一个特判
平衡树随便选一个就可以了


#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
cs double alpha = 0.8;
cs int N = 2e5 + 5, M = 6e6 + 5;
cs int mod = 1e9;
int TEST, n, r[N];
ll ans;
namespace SBT{ // 替罪羊
int ch[M][2], fa[M], siz[M], val[M]; int node;
int sta[M], top;
int a[M], hd;
#define ls ch[x][0]
#define rs ch[x][1]
int ck(){ return top ? sta[top] : node + 1; }
int newnode(){ return top ? sta[top--] : ++node; }
void bin(int x){ ls = rs = 0; sta[++top] = x;}
void pia(int x){ if(ls) pia(ls); if(rs) pia(rs); bin(x); }
int gc(int x){ return ch[fa[x]][1] == x; }
struct sbt{
int rt;
void re(int x){ if(ls) re(ls); a[++hd] = x; if(rs) re(rs); }
void pushup(int x){ siz[x] = siz[ls] + siz[rs] + 1; }
int build(int l, int r){
if(l > r) return 0; int mid = (l+r) >> 1, x = a[mid];
ls = build(l, mid - 1); rs = build(mid + 1, r);
fa[ls] = fa[rs] = x; pushup(x); return x;
}
void ins(int v){
int hi = -1, p = 0;
for(int x = rt; x; x = ch[p][val[p] <= v]){
p = x; ++siz[p]; if(fa[p] && fa[p] != rt && hi == -1 && siz[fa[p]] * 4 <= siz[p] * 5) hi = fa[p];
} int nx = newnode(); fa[nx] = p; ch[p][val[p] <= v] = nx; val[nx] = v; siz[nx] = 1;
// rebuild
if(hi != -1){ int f = fa[hi]; int k = gc(hi); hd = 0; re(hi); ch[f][k] = build(1, hd); fa[ch[f][k]] = f; }
}
int query(int v){
int ans = 0; for(int x = rt; x;){
if(val[x] <= v) ans += siz[ls] + 1, x = rs;
else x = ls;
} return ans;
}
int rebuild(int *a, int l, int r){
if(l > r) return 0; int mid = (l+r) >> 1; int x = newnode(); val[x] = a[mid];
ls = rebuild(a, l, mid - 1); rs = rebuild(a, mid + 1, r);
fa[ls] = fa[rs] = x; pushup(x); return x;
}
void clear(){pia(rt);}
void INIT(int *a, int len){ rt = rebuild(a, 0, len); fa[rt] = 0; }
};
}
namespace TREE{
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
}
int ndep[N];
vector<int> son[N];
struct data{
int fa, dis; SBT::sbt T;
}; vector<data> v[N];
typedef vector<data>::iterator Data;
typedef vector<int>::iterator Int;
typedef vector<SBT::sbt>::iterator Sbt;
vector<SBT::sbt> nw[N];
SBT::sbt T[N];
int top, *mdep, ret;
int siz[N], mxson[N];
bool cut[N];
int rt;

void getsz(int u, int fa){
siz[u] = 1;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa || !cut[t]) continue;
getsz(t, u); siz[u] += siz[t];
}
}

void getrt(int u, int fa, int S){
mxson[u] = 0;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa || !cut[t]) continue;
getrt(t, u, S); mxson[u] = max(mxson[u], siz[t]);
} mxson[u] = max(mxson[u], S - siz[u]);
if(mxson[rt] > mxson[u]) rt = u;
}

void dfs(int u, int fa, int dis, int g, int p){
son[g].push_back(u); v[u].push_back((data){g, dis, (SBT::sbt){p}});
mdep[++top] = dis - r[u];
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa || !cut[t]) continue;
dfs(t, u, dis + w[i], g, p);
}
}
void solve(int x){
getsz(x, 0); rt = 0; getrt(x, 0, siz[x]); int g = rt; cut[g] = false;
son[g].clear(); son[g].push_back(g);
if(siz[x] == 1){ ndep[0] = -r[x]; T[g].INIT(ndep, 0); return; }
mdep = ndep; top = -1; int Siz = 0;
for(int i = first[g]; i; i = nxt[i]){
if(!cut[to[i]]) continue; mdep = mdep + top + 1;
Siz += top + 1;
top = -1;
dfs(to[i], g, w[i], g, SBT::ck());
SBT::sbt tr; sort(mdep, mdep + top + 1);
tr.INIT(mdep, top); nw[g].push_back(tr);
} Siz += top + 1; ndep[Siz] = -r[g]; sort(ndep, ndep + Siz + 1);
T[g].INIT(ndep, Siz);
for(int i = first[g]; i; i = nxt[i]) if(cut[to[i]]) solve(to[i]);
}
void rebuild(int x){
for(Int it = son[x].begin(); it != son[x].end(); it++) cut[*it] = true;
for(Int it = son[x].begin(); it != son[x].end(); it++) T[*it].clear(); // 清空子树的平衡树
for(Int it = son[x].begin(); it != son[x].end(); it++){ // 清空子树所有点周围的平衡树
for(Sbt it1 = nw[*it].begin(); it1 != nw[*it].end(); it1++){
it1->clear();
} nw[*it].clear();
}
for(Int it = son[x].begin(); it != son[x].end(); it++){ // 清空子树中所有点存的祖先
if(*it != x){
while(1){
if(v[*it].rbegin()->fa == x){ // fa 是按顺序插入的
v[*it].pop_back(); break;
} else v[*it].pop_back();
}
}
} solve(x);
}

int ins(int x, int fa, int d, int wi){
add(x, fa, d); add(fa, x, d);
v[x] = v[fa];
for(Data it = v[x].begin(); it != v[x].end(); it++){
it->dis += d;
it->T.ins(it->dis - wi);
T[it->fa].ins(it->dis - wi);
}
SBT::sbt tr;
ndep[0] = d - wi;
tr.INIT(ndep, 0);
v[x].push_back((data){fa, d, tr});
nw[fa].push_back(tr);
T[fa].ins(d - wi);
ndep[0] = - wi;
T[x].INIT(ndep, 0);
son[x].push_back(x);
for(Data it = v[x].begin(); it != v[x].end(); ++it) son[it->fa].push_back(x);
Data it1 = v[x].begin(), it2 = it1; ++it2;
for(;it2 != v[x].end(); ++it2, ++it1){
if(son[it2->fa].size() * 5 >= son[it1->fa].size() * 4){ rebuild(it1->fa); break; }
}
int ans = T[x].query(wi) - 1; // 到 p 的点单独查
for(Data it = v[x].begin(); it != v[x].end(); ++it)
ans += T[it->fa].query(wi - it->dis) - it->T.query(wi - it->dis);
return ans;
}
void INIT(int wi){
ndep[0] = -wi; T[1].INIT(ndep, 0); son[1].push_back(1);
}
}
int main(){
TEST = read();
n = read();
TREE::mxson[0] = n + 1;
for(int i = 1; i <= n; i++){
ll fa = (read() ^ (ans % mod)), d = read(); r[i] = read();
if(i == 1){ TREE::INIT(r[i]); puts("0"); continue; }
ans += (ll)TREE::ins(i, fa, d, r[i]); cout << ans << '\n';
} return 0;
}