题面
解法
- 题目就是给你一棵树,有点权,每次操作修改一个点的点权,或者查询一条链上的第K大点权。简单来说就是树上带修权值第K大。
- 考虑不在树上,而是在区间上,如果没有修改的化,就是一个裸的主席树
- 加上修改的化,可以发现每次的修改,相当于在[L, R]的区间上都去掉原来权值的影响,在加上新修改的权值的影响,但是直接操作的复杂度是O(n)的。考虑树套树,用一个BIT维护区间的修改操作,每一个BIT上的点拎着的线段树表示[i - lowbit(i), i]上的信息。具体操作可以用差分,分别操作[1, L - 1]和[1, R]两个区间实现。
- 询问的话,同样运用差分的思想,二分答案的同时,用主席树的L-1,R两个历史版本询问。
- 考虑拓展到树上
- 有一个经常想到的思想,每一个点的线段树表示1 -> 这个点的路径上的权值。那么对于树上的一条链(u, v),这条链上的信息,相当于u + v - Lca(u,v) - fa[Lca(u, v)],这里的加减表示线段树上记录的值的加减。
- 那么对于一次u的修改操作,相当与把u子树内的每一棵线段树都操作一次。那么可以用Dfs序维护,使得u的子树的标号连续,用区间的方法完成。
- 细节不是很多,应该蛮好写的。
程序
#include
using namespace std;
#define LL long long
#define LD long double
template <typename T> void read(T &x)
{
x = 0; int f = 1; char c = getchar();
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for ( ; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
const int N = 80010;
int n, m, x, y, z, a[N];
char opt;
struct Segment_Tree
{
int rt[N], s[N * 300], lc[N * 300], rc[N * 300];
int q[N << 3], tot, top, now, su, sv, sp, sf;
void Modify(int &k, int l, int r, int w, int v)
{
if (!k) k = ++tot;
s[k] += v;
if (l == r) return;
int mid = l + r >> 1;
if (w <= mid) Modify(lc[k], l, mid, w, v);
else Modify(rc[k], mid + 1, r, w, v);
}
int Query(int k, int l, int r, int w)
{
if (!k) return 0;
if (w >= r) return s[k];
int mid = l + r >> 1;
if (w <= mid) return Query(lc[k], l, mid, w);
else return s[lc[k]] + Query(rc[k], mid + 1, r, w);
}
int Kth(int u, int v, int p, int f, int k)
{
top = 0;
for (int i = u; i; i -= i & -i) q[++top] = rt[i]; su = top;
for (int i = v; i; i -= i & -i) q[++top] = rt[i]; sv = top;
for (int i = p; i; i -= i & -i) q[++top] = rt[i]; sp = top;
for (int i = f; i; i -= i & -i) q[++top] = rt[i]; sf = top;
int L = 0, R = 1e8;
for (int mid = L + R >> 1, sum; L < R; mid = L + R >> 1)
{
sum = 0;
for (int i = 1; i <= su; ++i) sum += s[lc[q[i]]];
for (int i = su + 1; i <= sv; ++i) sum += s[lc[q[i]]];
for (int i = sv + 1; i <= sp; ++i) sum -= s[lc[q[i]]];
for (int i = sp + 1; i <= sf; ++i) sum -= s[lc[q[i]]];
if (sum >= k)
{
for (int i = 1; i <= top; ++i) q[i] = lc[q[i]];
R = mid;
}
else
{
for (int i = 1; i <= top; ++i) q[i] = rc[q[i]];
L = mid + 1, k -= sum;
}
if (L == R) return L;
}
return R;
}
} T;
struct BIT
{
void Modify(int x, int w, int v)
{
for (int i = x; i <= n; i += i & -i) T.Modify(T.rt[i], 0, 1e8, w, v);
}
int Query(int l, int r, int k)
{
int ans = 0;
for (int i = r; i; i -= i & -i) ans += T.Query(T.rt[i], 0, 1e8, k);
for (int i = l - 1; i; i -= i & -i) ans -= T.Query(T.rt[i], 0, 1e8, k);
return ans;
}
int Kth(int u, int v, int p, int fp, int k) { return T.Kth(u, v, p, fp, k); }
} B;
struct edge { int to, nxt; } e[N << 1];
const int LG = 16;
int fa[LG + 2][N];
int fir[N], dfn[N], id[N], dp[N], lst[N];
int cnt, tim, K;
void Ade(int u, int v)
{
e[++cnt] = (edge) { v, fir[u] }, fir[u] = cnt;
e[++cnt] = (edge) { u, fir[v] }, fir[v] = cnt;
}
void Dfs(int u, int f)
{
dfn[u] = ++tim, id[tim] = u, dp[u] = dp[f] + 1;
fa[0][u] = f;
for (int i = 1; i <= LG; ++i)
fa[i][u] = fa[i - 1][fa[i - 1][u]];
for (int i = fir[u]; i; i = e[i].nxt)
if (e[i].to != f) Dfs(e[i].to, u);
lst[u] = tim + 1;
}
int Lca(int u, int v)
{
if (dp[u] > dp[v]) swap(u, v);
for (int i = LG; i >= 0; --i)
if (dp[fa[i][v]] >= dp[u]) v = fa[i][v];
if (u == v) return u;
for (int i = LG; i >= 0; --i)
if (fa[i][u] != fa[i][v]) u = fa[i][u], v = fa[i][v];
return fa[0][u];
}
int main()
{
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i < n; ++i) read(x), read(y), Ade(x, y);
Dfs(1, 0);
for (int i = 1; i <= n; ++i)
B.Modify(dfn[i], a[i], 1),
B.Modify(lst[i], a[i], -1);
while (m--)
{
read(K), read(x), read(y);
if (K == 0)
{
B.Modify(dfn[x], a[x], -1);
B.Modify(lst[x], a[x], 1);
a[x] = y;
B.Modify(dfn[x], a[x], 1);
B.Modify(lst[x], a[x], -1);
}
else
{
z = Lca(x, y);
K = dp[x] + dp[y] - 2 * dp[z] - K + 2;
if (K <= 0) cout << "invalid request!\n";
else cout << B.Kth(dfn[x], dfn[y], dfn[z], dfn[fa[0][z]], K) << '\n';
}
}
return 0;
}