BZOJ 4867: [Ynoi2017]舌尖上的由乃
转化成dfs序上的问题,变成区间加和区间kth
可以用分块+二分,通常复杂度为 \(O(m\sqrt{n\log n}\log n)\),这道题里被卡掉了
设块大小为 \(B\),每一块内保持有序
修改时整块打上标记,零散块就现在有序表里分裂成两个序列,一个在修改的区间内,一个在修改的区间外,区间内加上值,再将两个序列归并,复杂度为 \(O(B)\)
查询时二分答案,整块里根据标记在有序表上二分,零散块再次将有序表分裂成值在区间内的一个有序表,然后再在这个新的有序表上二分,复杂度为 \(O(\dfrac{n}{B}\log^2 n)\)
当 \(B = \sqrt{n} \log n\) 时复杂度最优
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define pii pair<int, int>
#define lp p << 1
#define rp p << 1 | 1
#define mid ((l + r) >> 1)
#define ll long long
#define db double
#define rep(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=b-1;i>=a;i--)
#define Edg int ccnt=1,head[N],to[N*2],ne[N*2];void addd(int u,int v){to[++ccnt]=v;ne[ccnt]=head[u];head[u]=ccnt;}void add(int u,int v){addd(u,v);addd(v,u);}
#define Edgc int ccnt=1,head[N],to[N*2],ne[N*2],c[N*2];void addd(int u,int v,int w){to[++ccnt]=v;ne[ccnt]=head[u];c[ccnt]=w;head[u]=ccnt;}void add(int u,int v,int w){addd(u,v,w);addd(v,u,w);}
#define es(u,i,v) for(int i=head[u],v=to[i];i;i=ne[i],v=to[i])
const int MOD = 1e9 + 7;
void M(int &x) {if (x >= MOD)x -= MOD; if (x < 0)x += MOD;}
int qp(int a, int b = MOD - 2) {int ans = 1; for (; b; a = 1LL * a * a % MOD, b >>= 1)if (b & 1)ans = 1LL * ans * a % MOD; return ans % MOD;}
int gcd(int a, int b) { while (b) { a %= b; std::swap(a, b); } return a; }
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline char getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline int _() {
int x = 0, f = 1; char ch = getc();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getc(); }
while (ch >= '0' && ch <= '9') { x = x * 10ll + ch - 48; ch = getc(); }
return x * f;
}
const int N = 1e5 + 7;
int n, m, len, in[N], out[N], dfn, tol, limit, B, num, L[100], R[100], tag[100], belong[N];
std::pii a[N << 1];
Edgc
void dfs(int u, int f) {
in[u] = ++dfn;
a[dfn] = std::pii(f, dfn);
es(u, i, v) dfs(v, f + c[i]);
out[u] = dfn;
}
void build() {
B = sqrt(n) * log(n) / log(2) + 5;
num = n / B;
if (n % B) num++;
rep (i, 1, num + 1) {
L[i] = (i - 1) * B + 1, R[i] = std::min(i * B, n);
rep (j, L[i], R[i] + 1) belong[j] = i;
std::sort(a + L[i], a + R[i] + 1);
}
}
std::pii inq[N], outq[N];
void update(int p, int l, int r, int v) {
int cnt1 = 0, cnt2 = 0;
rep (i, L[p], R[p] + 1) {
if (a[i].se >= l && a[i].se <= r) inq[++cnt1] = a[i];
else outq[++cnt2] = a[i];
}
rep (i, 1, cnt1 + 1) inq[i].fi += v;
int cur = L[p], i = 1, j = 1;
while (i <= cnt1 && j <= cnt2)
a[cur++] = inq[i] < outq[j] ? inq[i++] : outq[j++];
while (i <= cnt1) a[cur++] = inq[i++];
while (j <= cnt2) a[cur++] = outq[j++];
}
void update(int l, int r, int v) {
int p = belong[l], q = belong[r];
if (p == q) {
update(p, l, r, v);
return;
}
rep (i, p + 1, q) tag[i] += v;
update(p, l, R[p], v); update(q, L[q], r, v);
}
int query(int p, int v) {
if (L[p] > R[p]) return 0;
v -= tag[p];
int l = L[p], r = R[p], ans = L[p] - 1;
while (l <= r) {
if (a[mid].fi <= v) ans = mid, l = mid + 1;
else r = mid - 1;
}
return ans - L[p] + 1;
}
int kth(int x, int y, int k) {
if (k > y - x + 1) return -1;
int p = belong[x], q = belong[y];
int st = n + 1, en = n;
rep (i, L[p], R[p] + 1) if (a[i].se >= x && a[i].se <= y) a[++en] = a[i];
L[num + 1] = st, R[num + 1] = en, tag[num + 1] = tag[p];
st = en + 1;
if (p < q) {
rep (i, L[q], R[q] + 1) if (a[i].se <= y) a[++en] = a[i];
}
L[num + 2] = st, R[num + 2] = en, tag[num + 2] = tag[q];
int l = 0, r = limit, ans = 0;
while (l <= r) {
int temp = query(num + 1, mid) + query(num + 2, mid);
rep (i, p + 1, q) temp += query(i, mid);
if (temp >= k) ans = mid, r = mid - 1;
else l = mid + 1;
}
return ans;
}
int main() {
n = _(), m = _(), len = _();
rep (i, 2, n + 1) {
int p = _(), cc = _(); limit += cc;
addd(p, i, cc);
}
dfs(1, 0);
build();
for (int opt, u, k; m--; ) {
opt = _(), u = _(), k = _();
if (opt == 2) {
update(in[u], out[u], k), limit += k;
} else {
printf("%d\n", kth(in[u], out[u], k));
}
}
return 0;
}