题解【USACO12DEC 逃跑的BarnRunning Away From…】

折月煮酒 提交于 2020-01-20 10:32:31

期末考前写题解,\(rp++! \ rp++! \ rp++!\)
\[ description \]
给出一个\(1\) 为根的边带权有根树,给定一个参数 \(L\) ,问每个点的子树中与它距离小于等于 \(L\) 的节点个数。
\[ solution \]
有关子树内的统计,肯定能联想到 线段树合并 吧。

\(d[u]\) 表示根节点到 \(u\) 的距离,该数组可通过一次遍历求出。

则对于每个点 \(u\) ,它的贡献就是满足下式的点对数量:
\[ d[v]-d[u]\leq L \ \{ v\in\operatorname{subtree}(u) \} \]
移项,可化简为:
\[ d[v]\leq d[u]+L \ \{ v\in\operatorname{subtree}(u) \} \]
那么问题就转化成了:

令点 \(u\) 的点权为 \(d[u]\)。对于每个点 \(u\) ,求出该点的子树中点权小于等于 \(d[u]+L\) 的节点个数

然后发现是一道 线段树合并 板子题,直接码上就行。

需注意的是,这个 \(L\)\(d[~]\) 的范围有 \(1e18\) ,所以我们需要离散化一下防止 \(MLE\)

我是把每个 \(d[u]\) 以及 \(d[u]+L\) 都扔进去离散化了,如果有哪位 \(dalao\) 有更优秀的离散化方法,可以评论在博客下方,我会感激不尽。
\[ code \]

#include<cstdio>
#include<algorithm>

#define RI register int

using namespace std;

const int N=200100,M=200100,MLOGN=10001000;

int n,m;
long long L;

int Etot,head[N],ver[M],Next[M];
long long edge[M];

void add(int u,int v,long long w)
{
    ver[++Etot]=v;    edge[Etot]=w;    Next[Etot]=head[u];    head[u]=Etot;
}

long long d[N];

int len;
long long mapval[1000100];

void discrete()
{
    sort(mapval+1,mapval+1+len);
    len=unique(mapval+1,mapval+1+len)-mapval-1;
}

int query(long long x)
{
    return lower_bound(mapval+1,mapval+1+len,x)-mapval;
}

int tot,root[N];
struct SegmentTree{
    int lc,rc;
    int cnt;
}t[MLOGN];

int New()
{
    tot++;
    t[tot].lc=t[tot].rc=t[tot].cnt=0;
    return tot;
}

void insert(int &p,int l,int r,int delta,int val)
{
    if(!p)p=New();
    t[p].cnt+=val;
    if(l==r)return;
    int mid=(l+r)/2;
    if(delta<=mid)
        insert(t[p].lc,l,mid,delta,val);
    else
        insert(t[p].rc,mid+1,r,delta,val);
}

int merge(int p,int q)
{
    if(!p||!q)return p^q;
    t[p].cnt+=t[q].cnt;
    t[p].lc=merge(t[p].lc,t[q].lc);
    t[p].rc=merge(t[p].rc,t[q].rc);
    return p;
}

int ask(int p,int l,int r,int s,int e)
{
    if(!p)return 0;
    if(s<=l&&r<=e)return t[p].cnt;
    int mid=(l+r)/2;
    int val=0;
    if(s<=mid)
        val+=ask(t[p].lc,l,mid,s,e);
    if(mid<e)
        val+=ask(t[p].rc,mid+1,r,s,e);
    return val;
}

void dfs1(int u)
{
    mapval[++len]=d[u],mapval[++len]=d[u]+L;
    for(RI i=head[u];i;i=Next[i])
    {
        int v=ver[i];
        long long w=edge[i];
        d[v]=d[u]+w;
        dfs1(v);
    }
}

int ans[N];

void dfs2(int u)
{
    for(RI i=head[u];i;i=Next[i])
    {
        int v=ver[i];
        dfs2(v);
        root[u]=merge(root[u],root[v]);
    }
    insert(root[u],1,len,query(d[u]),1);
    ans[u]=ask(root[u],1,len,1,query(d[u]+L));
}

int main()
{
    scanf("%d%lld",&n,&L);

    for(RI i=2;i<=n;i++)
    {
        int fa;
        long long w;
        scanf("%d%lld",&fa,&w);
        add(fa,i,w);
    }

    dfs1(1);

    discrete();

    dfs2(1);

    for(RI i=1;i<=n;i++)
        printf("%d\n",ans[i]);

    return 0;
}

\[ thanks \ for \ watching \]

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!