线段树+Dfs序【p2982】[USACO10FEB]慢下来Slowing down

本文探讨了一个有趣的问题场景,即如何利用线段树数据结构来优化奶牛赛跑过程中的速度调整次数计算。在一个由N个牧场构成的树状结构中,每只奶牛从粮仓出发前往其私人牧场,途中可能需要放慢速度以避免打扰已经到达牧场的同伴。文章详细解释了使用线段树维护dfs序和进行区间修改的算法思路,以及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

每天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会放慢自己的速度,防止打扰他的朋友。 考虑如下的牧场结构(括号内的数字代表了牧场的所有者)。 img

Input

第1行 : 一个正整数N

第2…N行: 第i+1行包括一对正整数A_i,B_i

第N+1..N+N行: 第 N+i行 包括一个正整数: P_i

Output

第一行到第N行:第i行表示第i只奶牛需要被放慢的次数

线段树维护\(dfs\)序.

单点查询.区间修改问题.

考虑到一头牛到达其私人牧场的时候,经过这个点的路径均会影响到这头牛吃草.(即需要放慢一次速度).

而,什么样的路径又会经过这个点?通向其子树的点!

所以对其子树进行区间修改,即\([dfn[x],dfn[x]+size[x]-1]\)

其中\(size[x]\)代表\(x\)的子树大小.

然后每次查询修改的时候记得下放\(tag\)标记.

查询即单点查询某个点的\(dfs\)序即可.

代码

#include<cstdio>
#include<cctype>
#define ls o<<1
#define rs o<<1|1
#define N 100008
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int head[N],tot;
struct cod{int u,v;}edge[N<<1];
inline void add(int x,int y)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    head[x]=tot;
}
int dfn[N],idx,size[N];
void dfs(int u,int fa)
{
    dfn[u]=++idx;size[u]=1;
    for(R int i=head[u];i;i=edge[i].u)
    {
        if(edge[i].v==fa)continue;
        dfs(edge[i].v,u);
        size[u]+=size[edge[i].v];
    }
}
int tr[N<<2],n;
inline void down(int o)
{
    if(tr[o])
    {
        tr[ls]+=tr[o];
        tr[rs]+=tr[o];
        tr[o]=0;
        return;
    }
}
void change(int o,int l,int r,int x,int y)
{
    if(x<=l and y>=r){tr[o]++;return;}
    down(o);
    int mid=(l+r)>>1;
    if(x<=mid)change(ls,l,mid,x,y);
    if(y>mid)change(rs,mid+1,r,x,y);
}
int query(int o,int l,int r,int x)
{
    if(l==r)return tr[o];
    down(o);
    int mid=(l+r)>>1;
    if(x<=mid)return query(ls,l,mid,x);
    return query(rs,mid+1,r,x); 
}
int main()
{
    in(n);
    for(R int i=1,x,y;i<n;i++)
    {
        in(x),in(y);
        add(x,y),add(y,x);
    }
    dfs(1,0);
    for(R int i=1,x;i<=n;i++)
    {
        in(x);
        printf("%d\n",query(1,1,n,dfn[x]));
        change(1,1,n,dfn[x],dfn[x]+size[x]-1);
    }
}

转载于:https://www.cnblogs.com/-guz/p/9846264.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值