蒟蒻的第一篇博客 [CTSC2008]网络管理

蒟蒻的第一篇博客[CTSC2008]网络管理

题面

解法

  • 题目就是给你一棵树,有点权,每次操作修改一个点的点权,或者查询一条链上的第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<bits/stdc++.h>
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()
{
//	freopen("test.in", "r", stdin);
//	freopen("test.out", "w", stdout);
    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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值