CF 696E ...Wait for it... 链剖练手题

题目大意

给定一颗 N 个点的树,现在有M个物品,每个物品一开始都在节点 Ai 上,权值为 i 。现在有两种操作,先读入Ord
Ord=1 :再读入三个整数 u,v,Lim 表示从 u v这条路径上从小到大把最多 Lim 个物品从树上取出,并且从小到大输出。
Ord=2 :再读入两个数 u,Val 表示给在 u 这棵子树的所有物品加上权值Val

N,M,Q105
Val109

解题思路

一道链剖的裸题!
对于一个点上的物品,它们权值的大小关系是确定的。那么我们只要在一开始把物品都丢到节点 Ai 上,排个序,权值最小的作为这个点的权值。然后对于第一种操作,我们只需依次的拿出 Lim 个点,即每次都查询区间最小值,然后把这个物品从节点处删掉,然后那个节点再找一个剩余节点中权值最小的作为新的权值,因为每个物品只会被删一次所以复杂度是 O(Nlog2N) 的。第二种操作就很简单了,是经典的区间加。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;
typedef long long LL;

const int MAXN = 1e5 + 5;
const LL Inf = 1e15 + 7;

struct Node {
    LL Val;
    int Num;
    Node (LL val, int num){Val = val, Num = num;}
    Node (){}
};

struct Tree {
    LL add; 
    int Num;
    Node Min;
} Tr[MAXN * 4];

vector<int> P[MAXN];
int N, M, Q, time, Bel[MAXN], Size[MAXN], MSon[MAXN], L[MAXN], R[MAXN], Deep[MAXN], Pre[MAXN], Top[MAXN];
int tot, Last[MAXN], Go[MAXN * 2], Next[MAXN * 2];

void Link(int u, int v) {
    Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}

Node Min(Node A, Node B) {
    if (A.Val < B.Val) return A;
    if (A.Val > B.Val) return B;
    if (A.Num < B.Num) return A;
    return B;
}


void Basis(int Now, int Fa) {
    Size[Now] = 1, MSon[Now] = 0;
    Pre[Now] = Fa, Deep[Now] = Deep[Fa] + 1;
    for (int p = Last[Now]; p; p = Next[p]) {
        int v = Go[p];
        if (v == Fa) continue;
        Basis(v, Now);
        Size[Now] += Size[v];
        if (Size[v] > Size[MSon[Now]]) MSon[Now] = v;
    }
}

void Promote(int Now, int top) {
    if (!Now) return;
    L[Now] = ++ time, Bel[time] = Now;
    Top[Now] = top;
    Promote(MSon[Now], top);
    for (int p = Last[Now]; p; p = Next[p]) {
        int v = Go[p];
        if (v == Pre[Now] || v == MSon[Now]) continue;
        Promote(v, v);
    }
    R[Now] = time;
}

bool Cmp(int A, int B) {
    return A > B;
}

Node Get(int Now) {
    if (P[Now].empty()) return Node(Inf, Bel[Now]); 
    return Node(P[Now].back(), Bel[Now]);
}

Node Up(Node A, LL add) {
    A.Val += add;
    return A;
}

void MakeTag(int Now, LL add) {
    Tr[Now].add += add;
}

void Push(int Now, int l, int r) {
    if (l != r) {
        int Mid = (l + r) >> 1;
        MakeTag(Now * 2, Tr[Now].add);
        MakeTag(Now * 2 + 1, Tr[Now].add);      
        Tr[Now].Min = Min(Up(Tr[Now * 2].Min, Tr[Now * 2].add), Up(Tr[Now * 2 + 1].Min, Tr[Now * 2 + 1].add));
        Tr[Now].add = 0;
    }
}

void Modify(int Now, int l, int r, int lx, int rx, LL add) {
    Push(Now, l, r);
    if (l == lx && r == rx) {
        MakeTag(Now, add);
        return;
    }
    int Mid = (l + r) >> 1;
    if (rx <= Mid) Modify(Now * 2, l, Mid, lx, rx, add); else
    if (lx > Mid) Modify(Now * 2 + 1, Mid + 1, r, lx, rx, add); else {
        Modify(Now * 2, l, Mid, lx, Mid, add);
        Modify(Now * 2 + 1, Mid + 1, r, Mid + 1, rx, add);
    }
    Tr[Now].Min = Min(Up(Tr[Now * 2].Min, Tr[Now * 2].add), Up(Tr[Now * 2 + 1].Min, Tr[Now * 2 + 1].add));
}

void Build(int Now, int l, int r) {
    if (l == r) {
        Tr[Now].Min = Get(l);
        return;
    }
    int Mid = (l + r) >> 1;
    Build(Now * 2, l, Mid), Build(Now * 2 + 1, Mid + 1, r);
    Tr[Now].Min = Min(Tr[Now * 2].Min, Tr[Now * 2 + 1].Min);
}

Node Ask(int Now, int l, int r, int lx, int rx) {
    Push(Now, l, r);
    if (l == lx && r == rx) return Up(Tr[Now].Min, Tr[Now].add);
    int Mid = (l + r) >> 1;
    if (rx <= Mid) return Up(Ask(Now * 2, l, Mid, lx, rx), Tr[Now].add); else
    if (lx > Mid) return Up(Ask(Now * 2 + 1, Mid + 1, r, lx, rx), Tr[Now].add); else 
        return Up(Min(Ask(Now * 2, l, Mid, lx, Mid), Ask(Now * 2 + 1, Mid + 1, r, Mid + 1, rx)), Tr[Now].add);
}

int Ask(int u, int v) {
    Node Ans = Node(Inf, 0);
    while (Top[u] != Top[v]) {
        if (Deep[Top[u]] < Deep[Top[v]]) swap(u, v);
        Ans = Min(Ans, Ask(1, 1, N, L[Top[u]], L[u]));
        u = Pre[Top[u]];
    }
    if (L[u] > L[v]) swap(u, v);
    Ans = Min(Ans, Ask(1, 1, N, L[u], L[v]));
    return Ans.Num;
}

void Del(int Now, int l, int r, int Side) {
    Push(Now, l, r);
    if (l == r) {
        P[l].pop_back();
        Tr[Now].Min = Get(l);
        return;
    }
    int Mid = (l + r) >> 1;
    if (Side <= Mid) Del(Now * 2, l, Mid, Side); else
        Del(Now * 2 + 1, Mid + 1, r, Side); 
    Tr[Now].Min = Min(Up(Tr[Now * 2].Min,Tr[Now * 2].add), Up(Tr[Now * 2 + 1].Min, Tr[Now * 2 + 1].add));
}

void Query(int u, int v, int Lim) {
    static int D[MAXN];
    int top = 0;
    for (int i = 1; i <= Lim; i ++) {
        int Now = L[Ask(u, v)];
        if (!Now) break;
        D[++ top] = P[Now].back();
        Del(1, 1, N, Now);
    }
    printf("%d ", top);
    for (int i = 1; i <= top; i ++) printf("%d ", D[i]);
    printf("\n");
}

void Prepare() {
    Basis(1, 0);
    Promote(1, 1);
}

int main() {
    scanf("%d%d%d", &N, &M, &Q);
    for (int i = 1; i < N; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        Link(u, v), Link(v, u);
    }
    Prepare();
    for (int i = 1; i <= M; i ++) {
        int Val;
        scanf("%d", &Val);
        P[L[Val]].push_back(i);
    }

    for (int i = 1; i <= N; i ++) sort(P[i].begin(), P[i].end(), Cmp);
    Build(1, 1, N);
    for (int i = 1; i <= Q; i ++) {
        int Ord;
        scanf("%d", &Ord);
        if (Ord == 1) {
            int u, v, Lim;
            scanf("%d%d%d", &u, &v, &Lim);
            Query(u, v, Lim);
        } else {
            int u, add;
            scanf("%d%d", &u, &add);
            Modify(1, 1, N, L[u], R[u], add);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值