【USACO10FEB】Slowing down G

题目描述

每天Farmer John的N头奶牛(1 <= N <= 100000,编号1…N)从粮仓走向他的自己的牧场。牧场构成了一棵树,粮仓在1号牧场。恰好有N-1条道路直接连接着牧场,使得牧场之间都恰好有一条路径相连。第i条路连接着A_i,B_i,(1 <= A_i <= N; 1 <= B_i <= N)。 奶牛们每人有一个私人牧场P_i (1 <= P_i <= N)。粮仓的门每次只能让一只奶牛离开。耐心的奶牛们会等到他们的前面的朋友们到达了自己的私人牧场后才离开。首先奶牛1离开,前往P_1;然后是奶牛2,以此类推。

当奶牛i走向牧场P_i时候,他可能会经过正在吃草的同伴旁。当路过已经有奶牛的牧场时,奶牛i会放慢自己的速度,防止打扰他的朋友。

FJ想要知道奶牛们总共要放慢多少次速度。

链接


传送门

分析

涉及单点修改貌似用树状数组就行,不过数组数组可以的线段树也可以就是。

代码

#include <iostream>
#include <cctype>
#include <algorithm>
#include <cstdio>
#define mid (l + r >> 1)
using namespace std;
const int N = 1e5 + 10;
int n, p, tot, cnt;
int head[N];
int dep[N], siz[N], par[N], son[N];
int dfn[N], top[N];

struct edge{
    int to, nxt;
}e[N << 1];

inline int read(){
    int x = 0, op = 1;
    char ch = getchar();
    while (!isdigit(ch)){
        if (ch == '-') op = -1;
        ch = getchar();
    }
    while (isdigit(ch)){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * op;
}

inline void add(int u, int v){
    e[++tot] = {v, head[u]};
    head[u] = tot;
}

void dfs1(int u, int fa){
    par[u] = fa;
    siz[u] = 1;
    dep[u] = dep[fa] + 1;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]])
            son[u] = v;
    }
}

void dfs2(int u, int tp){
    dfn[u] = ++cnt;
    top[u] = tp;
    if (son[u])
        dfs2(son[u], tp);

    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v != son[u] && v != par[u])
            dfs2(v, v);
    }
}

int sum[N << 2];
void push_up(int i){
    sum[i] = sum[i << 1] + sum[i << 1|1];
}

void modify_point(int i, int l, int r, int pos){
    if (l == r) {
        sum[i] = 1;
        return;
    }

    if (pos <= mid)
        modify_point(i << 1, l, mid, pos);
    else
        modify_point(i << 1|1, mid + 1, r, pos);
    push_up(i);
}

int query_interval(int i, int l, int r, int crtl, int crtr){
    if (l > crtr || r < crtl)
        return 0;

    if (l >= crtl && r <= crtr)
        return sum[i];

    int res = 0;
    if (mid >= crtl)
        res += query_interval(i << 1, l, mid, crtl, crtr);
    if (mid < crtr)
        res += query_interval(i << 1|1, mid + 1, r, crtl, crtr);
    return res;
}

int tree_sum(int u, int v){
    int res = 0;
    while (top[u] != top[v]){
        if (dep[top[u]] < dep[top[v]])
            swap(u, v);
        res += query_interval(1, 1, n, dfn[top[u]], dfn[u]);
        u = par[top[u]];
    }

    if (dep[u] > dep[v])
        swap(u, v);
    res += query_interval(1, 1, n, dfn[u], dfn[v]);
    return res;
}

int main() {
    n = read();
    for (int i = 1, x, y; i <= n - 1; ++i) {
        x = read(), y = read();
        add(x, y), add(y, x);
    }
    dfs1(1, 0);
    dfs2(1, 1);

    for (int i = 0; i < n; ++i) {
            p = read();
            printf("%d\n", tree_sum(1, p));
            modify_point(1, 1, n, dfn[p]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值