关于求答案:
对于每一个分治中心
g
g
g,求经过它的点对
(
u
,
v
)
(u,v)
(u,v) 的贡献
需要
d
i
s
(
u
,
g
)
+
d
i
s
(
v
,
g
)
≤
r
u
+
r
v
dis(u,g)+dis(v,g)\le r_u+r_v
dis(u,g)+dis(v,g)≤ru+rv
也就是
d
i
s
(
u
,
g
)
−
r
u
≤
r
v
−
d
i
s
(
v
,
g
)
dis(u,g)-r_u\le r_v-dis(v,g)
dis(u,g)−ru≤rv−dis(v,g)
可以用平衡树维护一波
d
i
s
(
u
,
g
)
−
r
u
dis(u,g)-r_u
dis(u,g)−ru,然后就是查询比一个数小的个数
但这样是算重了的,因为
u
,
v
u,v
u,v 可能在
g
g
g 下方的同一棵子树
然后还需要维护一棵减去贡献的平衡树
不用想复杂,减去的贡献
(
u
,
v
)
(u,v)
(u,v) 当且仅当
(
u
,
v
)
(u,v)
(u,v) 在切掉
g
g
g 的情况下还在一个联通块
于是可以对这些连通块维护平衡树存同样的值
也就是说当前点
u
u
u 的贡献,就是在每一个祖先
g
g
g 查一个答案,然后在
g
g
g 割掉后
u
u
u 的联通块的平衡树中查一个答案减掉
为了避免点分树每次跳
f
a
fa
fa 的繁琐讨论
我们每个点开一个
v
e
c
t
o
r
vector
vector 存
(
f
a
,
d
i
s
,
t
r
e
e
)
(fa,dis,tree)
(fa,dis,tree)
表示这个点的祖先,到祖先的距离以及切掉祖先后
u
u
u 所在的平衡树的编号
因为要插叶子,所以点分树会不平衡,类似替罪羊的思想,设一个平衡因子 α \alpha α,定期重构
重构点分树:
首先需要知道支持这个重构操作需要维护什么?
子树需要清空,每个点开
v
e
c
t
o
r
vector
vector 暴力维护子树中的结点
然后对于一个子孙的
(
f
a
,
d
i
s
,
t
r
e
e
)
(fa,dis,tree)
(fa,dis,tree) 的
t
r
e
e
tree
tree 我们需要把它清空,所以一个点要维护切掉它过后的
t
r
e
e
tree
tree 的集合,同样
v
e
c
t
o
r
vector
vector 暴力维护
需要清空的东西:
1.子树所有点的平衡树
2.子树所有点维护儿子的
v
e
c
t
o
r
vector
vector
3.子树用来减的平衡树
4.子树所有维护祖先的
v
e
c
t
o
r
vector
vector
5.维护割掉它剩下的联通块的平衡树
细节:发现祖先的 v e c t o r vector vector 是按顺序插的,深的在下面,于是弹一个点的祖先的时候可以暴力弹的 x x x
点分治
跟一般的点分治一样,
d
f
s
dfs
dfs 的时候插一下维护祖先的
v
e
c
t
o
r
vector
vector
然后暴力把
d
f
s
dfs
dfs 到的点插到自己的儿子集合,把
r
i
−
d
i
s
r_i-dis
ri−dis 存下来平衡树
b
u
i
l
d
build
build 一波
然后还要维护一圈的平衡树,把那一棵子树的点弄出来暴力
b
u
i
l
d
build
build 就可以了
插入:
把
x
x
x 挂在
u
u
u 上
先把
u
u
u 维护祖先的
v
e
c
t
o
r
vector
vector 复制给
x
x
x,然后将里面的每一个
d
i
s
dis
dis 加上
d
i
s
(
x
,
u
)
dis(x,u)
dis(x,u)
在祖先
g
g
g 的平衡树中插入
d
i
s
−
r
x
dis - r_x
dis−rx 的值
然后需要在
x
x
x 的
v
e
c
t
o
r
vector
vector 中插入
(
u
,
d
i
s
(
x
,
u
)
,
t
r
e
e
)
(u,dis(x,u),tree)
(u,dis(x,u),tree) 这个三元组
然后暴力跳祖先,把
x
x
x 加到它们的集合
中途遇到不平衡就找到最高的并重构
总结:
再次理清思路:
- 查询答案需要维护平衡树,同时需要容斥掉在一个子树不合法的情况,所以需要维护一个点割掉它剩下的一圈平衡树
- 一个点要查答案需要知道所有祖先,祖先的平衡树,祖先割掉后 x x x 属于的那个连通块的平衡树,用一个 v e c t o r vector vector 存
- 一个点要重构整个子树需要知道子树的所有点, v e c t o r vector vector 暴力维护,插入时更新
主要细节就是这些,第一个特判
平衡树随便选一个就可以了
#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;
}