Description
给定一棵
n
n
n 个点的树
T
T
T,边有边权
w
i
w_i
wi,树根为
1
1
1,设
dep
(
u
)
\operatorname{dep}(u)
dep(u) 为
u
u
u 的带权深度,初始时根节点的深度为
0
0
0。
执行
q
q
q 次操作分两种:
1 u k:求 u u u 的子树中第 k k k 小的 dep ( u ) \operatorname{dep}(u) dep(u),若不足 k k k 个点输出 − 1 -1 −1。2 u x:将 u u u 和它父亲之间边的权值加上 x x x,若 u = 1 u=1 u=1 则把根节点深度加 x x x。
保证 w i , x w_i,x wi,x 不大于一个给定常数 L L L。
Limitations
1
≤
n
,
q
≤
10
5
1\le n,q\le 10^5
1≤n,q≤105
1
≤
L
≤
10
1\le L\le 10
1≤L≤10
1
≤
u
≤
n
1\le u\le n
1≤u≤n
3
s
,
500
MB
3\text{s},500\text{MB}
3s,500MB
Solution
和
L
L
L 无关的解法。视值域
V
V
V 和
n
n
n 同阶。
首先算出每个点的
dep
\operatorname{dep}
dep 并拍到 dfs 序上,那么问题就变为序列
a
a
a 上区间加,区间第
k
k
k 小。
考虑使用 P5356 的 O ( n n log 2 n ) O(n\sqrt n \log^ 2n) O(nnlog2n) 做法:
对每个查询二分答案,问题变为区间加 x x x,求区间 ≤ x \le x ≤x 的数个数。
考虑分块,对每块维护一个块内排序后的数组 p i p_i pi。
查询时,整块在 p i p_i pi 上二分,散块直接暴力。
修改时,整块打标记,散块的 p i p_i pi 分成两部分归并。
然而这样实测过不去,需要如下优化:
- 二分答案范围没必要那么大,可以取 a l ∼ a r a_l\sim a_r al∼ar 的最值作为边界。
- 每次
check都要暴力算散块太慢了,可以在二分答案前,先取两边散块内的元素归并成有序序列 t t t,check时散块和整块一样在 t t t 上二分。 - 经分析得取 B = n log n B=\sqrt n\log n B=nlogn 最快。
然后加个快读快写就过了。
Code
6.67
KB
,
14.38
s
,
14.53
MB
(C++20
with
O2)
6.67\text{KB},14.38\text{s},14.53\text{MB}\;\texttt{(C++20 with O2)}
6.67KB,14.38s,14.53MB(C++20 with O2)
只放一部分.
constexpr int cap = 4e7 + 10;
int pool[cap], *ptr = pool;
template<class T>
inline T* alloc(int n) {
int siz = sizeof(T) / sizeof(int) * n;
T* res = (T*)ptr; ptr += siz;
return res;
}
constexpr int inf = 1.5e9;
struct Element {
int val, id;
inline Element() {}
inline Element(int _val, int _id) : val(_val), id(_id) {}
inline bool operator<(const Element& rhs) const { return val < rhs.val; }
};
struct Block {
int n, B, blocks;
int *a, *tag, *bel, *L, *R;
Element *sorted, *left, *right, *tmp;
inline Block() {}
inline Block(int _n, int *_a) : a(_a) {
n = _n;
B = sqrt(n) * log2(n) + 1;
blocks = (n + B - 1) / B;
sorted = alloc<Element>(n), bel = alloc<int>(n);
left = alloc<Element>(B), right = alloc<Element>(B), tmp = alloc<Element>(2 * B);
L = alloc<int>(blocks), R = alloc<int>(blocks), tag = alloc<int>(blocks);
for (int i = 0; i < blocks; i++) {
L[i] = i * B;
R[i] = min(L[i] + B, n) - 1;
for (int j = L[i]; j <= R[i]; j++) {
bel[j] = i;
sorted[j] = Element(a[j], j);
}
sort(sorted + L[i], sorted + R[i] + 1);
}
}
inline void brute_bound(int b, int l, int r, int& vl, int& vr) {
for (int i = l; i <= r; i++) {
chmin(vl, a[i] + tag[b]);
chmax(vr, a[i] + tag[b]);
}
}
inline pair<int, int> bound(int l, int r) {
int bl = bel[l], br = bel[r];
int vl = inf, vr = -inf;
if (bl == br) brute_bound(bl, l, r, vl, vr);
else {
brute_bound(bl, l, R[bl], vl, vr);
brute_bound(br, L[br], r, vl, vr);
for (int i = bl + 1; i < br; i++) {
chmin(vl, sorted[L[i]].val + tag[i]);
chmax(vr, sorted[R[i]].val + tag[i]);
}
}
return make_pair(vl, vr);
}
inline int single_merge(int b, int l, int r) {
int siz = 0;
for (int i = L[b]; i <= R[b]; i++)
if (sorted[i].id >= l && sorted[i].id <= r) {
tmp[siz++] = Element(sorted[i].val + tag[b], sorted[i].id);
}
return siz;
}
inline int partial_merge(int bl, int br, int l1, int r1, int l2, int r2) {
int lsiz = 0, rsiz = 0;
for (int i = L[bl]; i <= R[bl]; i++)
if (sorted[i].id >= l1 && sorted[i].id <= r1) {
left[lsiz++] = Element(sorted[i].val + tag[bl], sorted[i].id);
}
for (int i = L[br]; i <= R[br]; i++)
if (sorted[i].id >= l2 && sorted[i].id <= r2) {
right[rsiz++] = Element(sorted[i].val + tag[br], sorted[i].id);
}
merge(left, left + lsiz, right, right + rsiz, tmp);
return lsiz + rsiz;
}
inline int count(int bl, int br, int x, int siz) {
int res = upper_bound(tmp, tmp + siz, Element{x, 0}) - tmp;
for (int i = bl; i <= br; i++) {
res += upper_bound(
sorted + L[i], sorted + R[i] + 1,
Element{x - tag[i], 0}
) - sorted - L[i];
}
return res;
}
inline int kth(int l, int r, int k) {
const int len = r - l + 1;
if (k < 1 || k > len) return -1;
int siz = 0, bl = bel[l], br = bel[r];
if (bl == br) siz = single_merge(bl, l, r);
else siz = partial_merge(bl, br, l, R[bl], L[br], r);
auto [vl, vr] = bound(l, r);
if (k == 1) return vl;
if (k == len) return vr;
int res = -1;
while (vl <= vr) {
int mid = (vl + vr) >> 1;
if (count(bl + 1, br - 1, mid, siz) < k) vl = mid + 1;
else vr = (res = mid) - 1;
}
return res;
}
inline void brute_add(int b, int l, int r, int k) {
int lsiz = 0, rsiz = 0;
for (int i = L[b]; i <= R[b]; i++) {
if (i >= l && i <= r) a[i] += k;
if (sorted[i].id >= l && sorted[i].id <= r)
left[lsiz++] = Element(sorted[i].val + k, sorted[i].id);
else right[rsiz++] = sorted[i];
}
std::merge(left, left + lsiz, right, right + rsiz, sorted + L[b]);
}
inline void add(int l, int r, int k) {
const int bl = bel[l], br = bel[r];
if (bl == br) return brute_add(bl, l, r, k);
brute_add(bl, l, R[bl], k);
brute_add(br, L[br], r, k);
for (int i = bl + 1; i < br; i++) tag[i] += k;
}
};
1万+

被折叠的 条评论
为什么被折叠?



