P3066 [USACO12DEC]Running Away From the Barn G 树上差分+倍增/区间数点

复制代码
//题意:n个节点的树,每条边有边权,现在要求对于每一个结点u的子树中有多少个节点
//      满足到u点的距离不大于t
// 思路:树上差分思想还是比较好想到的,因为要满足dep[x]-dep[u]<=t,所以在x之上到
//       u之间的所有点对u点的答案都有贡献,所以上差分就好
// 
//       主要不是很熟练的的是如何对于每个询问求满足条件的x
//       1.第一种实现方法利用倍增,从节点u开始向下倍增(注意这里犯了一个很严重的错误,
//         我们以节点u为头向下倍增是不可行的,因为他会有多个儿子,也就是分叉。正确的
//         方法是对每一个x向上倍增,找满足条件的祖先节点)
// 
//           这样可以在logn步的规模内
//         跳到x节点。因为我们需要在每个节点都进行一次倍增查询,所以总的复杂度是
//         O(nlogn)。
//

//思路一
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
struct edge {
    int len, to;
    edge(int a = 0, int b = 0) {
        len = a, to = b;
    }
};
vector<edge> mp[N];
int n, t;
int lis[N][20], dep[N], ans[N];
void dfs(int x) {
    for (auto y : mp[x]) {
        if (y.to == lis[x][0]) continue;
        dep[y.to] = dep[x] + y.len;
        dfs(y.to);
    }
}
void dfs2(int y) {//当前遍历节点
    int x = y;
    for (int i = 18; i >= 0; i--) {
        if (lis[x][i] == 0) continue;
        if (dep[lis[x][i]] + t < dep[y]) continue;
        x = lis[x][i];
    }
    ans[y] += 1;
    ans[lis[x][0]] -= 1;
    for (auto w : mp[y]) {
        if (w.to == lis[y][0]) continue;
        dfs2(w.to);
        ans[y] += ans[w.to];
    }
}
signed main() {
    cin >> n >> t;
    for (int i = 2; i <= n; i++) {
        int a, b; cin >> a >> b;
        mp[i].push_back({ b,a });
        mp[a].push_back({ b,i });
        lis[i][0] = a;
    }

    for (int i = 1; i <= 18; i++) {
        for (int j = 1; j <= n; j++) {
            lis[j][i] = lis[lis[j][i - 1]][i - 1];
        }
    }
    lis[1][0] = 0;
    dfs(1); dep[1] = 0;
    dfs2(1);
    for (int i = 1; i <= n; i++)
        cout << ans[i] << endl;

    return 0;
}

/*
4 5
1 4 
2 3 
1 5 
*/

//思路:类似于区间数点问题,就是先dfs求出每个点的dep,然后按dep降序排序,删除和插入就行,这里代码中详细讲,这个思路有点妙啊
//思路二
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, id[N], r[N], now, ans[N];
pair<long long, int> a[N];
struct BIT
{
    int tree[N];
    void modify(int x, int val)
    {
        for (; x <= n; x += x & -x)
            tree[x] += val;
    }
    int query(int x)
    {
        int ans = 0;
        for (; x; x -= x & -x)
            ans += tree[x];
        return ans;
    }
}T;
//树状数组模板
vector<pair<int, long long>> mat[N];
void dfs(int k)
{
    id[k] = ++now;
    //dfs序
    for (auto e : mat[k])
    {
        a[e.first] = make_pair(a[k].first + e.second, e.first);
        dfs(e.first);
    }
    r[k] = now;
    //结束时间戳
}
int main()
{
    long long l;
    cin >> n >> l;
    for (int i = 2; i <= n; i++)
    {
        int p;
        long long w;
        cin >> p >> w;
        mat[p].push_back(make_pair(i, w));
    }
    a[1] = make_pair(0ll, 1);
    dfs(1);
    sort(a + 1, a + n + 1);
    int j = n;//j初始值为n,也就是最深
    for (int i = n; i; i--)
    {
        for (; a[j].first - a[i].first > l; j--)
            T.modify(id[a[j].second], -1);
        //删除超过距离超过L的点
        T.modify(id[a[i].second], 1);
        //插入当前点
        ans[a[i].second] = T.query(r[a[i].second]) - T.query(id[a[i].second] - 1);
        //统计子树答案
    }

    //这里删点的逻辑很妙,因为我们按照dep的降序的话,i和j有两种情况
    // 情况一:i和j是在同一条树链上的,那么dep[j]-dep[i]>l的话,j点是肯定要删除的
    // 情况二:i和j没在同一条树链上,这个时候dep[j]-dep[i]>l我们也把j删除了,因为既然对于i来说j已经不满足条件了,那么
    //         对于j这条树链上的其他点,j也无法做出贡献了(因为dep[其他点]<=dep[i])
    //
    for (int i = 1; i <= n; i++)
        cout << ans[i] << endl;
    return 0;
}
复制代码

 

posted @   Aacaod  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示