GDOI2016模拟8.16逃离牛棚

题目
现在正是Farmer John的农场里面挤奶的季节了!可是奶牛们全都跑了。FJ需要把他们都圈起来,需要你帮他搜寻奶牛。
FJ的农场里有一排N(1<=N<=200,000)个(编号为1..N)的草场,这N个农场由N-1条双向路连接。牛棚在草场1,而且牛棚能通向任意草场。
FJ的奶牛今天早上都在他们的草场上,但谁知道他们现在跑到哪里去了。FJ知道奶牛们只会从牛棚逃出去,而且他们太懒了所以不能跑超过L的距离。对于每个草场,FJ想知道奶牛们能从这个草场出发能到达的多少个不同的草场,使得奶牛们离牛棚更远。
注意:需要用64位整型存储表示距离的值(int64 in Pascal,long long in C/C++,long in Java)。

这题正难则反(这题是弱化版的,直接上就行,强化版的允许往祖先走,再往下其他分支走)

我们可以将每个点对其他点答案的贡献求出来,即往上距离L以内所有点答案+1,这个可以链剖+线段树(或用数组打标记前缀和,这个少个log)

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#define N 200001
using namespace std;
int n;
long long L;
int b[N][2],h[N],f[4*N],g[N],bz[N],fa[N][18],ans[N],bz1[N];
long long len[N][18];
struct node{
    long long dis;
    int v,next;
}a[N];
void ins(int x,int y,long long z){
    static int sum=0;
    a[++sum].v=y,a[sum].dis=z,a[sum].next=g[x],g[x]=sum;
} 
void init(){
    static int x;
    long long y;
    scanf("%d %lld",&n,&L);
    for (int i=2;i<=n;i++){
        scanf("%d %lld",&x,&y);
        fa[i][0]=x,len[i][0]=y;
        ins(x,i,y);
    }
}
void dfs(int x){
    for (int i=0;fa[fa[x][i]][i];fa[x][i+1]=fa[fa[x][i]][i],len[x][i+1]=len[x][i]+len[fa[x][i]][i],i++);
    for (int i=g[x];i;i=a[i].next){
        dfs(a[i].v);
        if (b[x][0]<b[a[i].v][0]+1)
            b[x][0]=b[a[i].v][0]+1,b[x][1]=a[i].v;
    }
}
void dfs1(int x){
    static int sum=0;
    bz1[bz[x]=++sum]=x;
    if (b[x][1])
        h[b[x][1]]=h[x],dfs1(b[x][1]);
    for (int i=g[x];i;i=a[i].next)
        if (a[i].v!=b[x][1])
            h[a[i].v]=a[i].v,dfs1(a[i].v);
}
void pre(){
    dfs(1);
    h[1]=1;
    dfs1(1);
}
void change(int l,int r,int s,int ll,int rr){
    if (rr<l||r<ll)return;
    if (ll<=l&&r<=rr){
        ++f[s];
        return;
    }
    change(l,(l+r)/2,s+s,ll,rr);
    change((l+r)/2+1,r,s+s+1,ll,rr);
}
void up(int x,int y){
    while (true)
        if (bz[h[x]]<=bz[y]){
            change(1,n,1,bz[y],bz[x]);
            return;
        }else
            change(1,n,1,bz[h[x]],bz[x]),x=fa[h[x]][0];
}
int get(int x){
    static int i;
    static long long y;
    y=0,i=17;
    while (y+len[x][0]<=L&&x!=1){
        for (;y+len[x][i]>L||!fa[x][i];i--);
        y+=len[x][i],x=fa[x][i];
    }
    return x;
}
void build(int l,int r,int s){
    if (l==r){
        ans[bz1[l]]=f[s];
        return;
    }
    f[s+s]+=f[s],f[s+s+1]+=f[s];
    build(l,(l+r)/2,s+s),build((l+r)/2+1,r,s+s+1);
}
void work(){
    for (int i=1;i<=n;i++)
        up(i,get(i));
    build(1,n,1);
}
void write(){
    for (int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
}
int main(){
    init();
    pre();
    work();
    write();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值