UOJ Logo SHYI的博客

博客

[JLOI2014]松鼠的新家

2016-07-21 20:47:29 By SHYI

题目大意:给你一棵树,要从编号为a[1]的节点走到编号为a[2]的节点再走到编号为a[3]的节点……一直走到编号为a[n]的节点。问每个节点最少访问多少次。

思路:将其进行轻重链剖分,则从a[i]走到a[i+1]实际上就是在几段重链的节点上+1,于是就用线段树来维护一下即可。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1200000
using namespace std;

int ans[M],to[M],head[M],next[M],vis[M],size[M],deep[M],id[M],top[M],sum[M],fa[M],a[M],cnt,dfn,n;

void add(int x,int y)
{
    to[++cnt]=y;
    next[cnt]=head[x];
    head[x]=cnt;
}

void dfs1(int x)
{
    size[x]=vis[x]=1;
    for (int i=head[x];i;i=next[i])
        if (!vis[to[i]])
        {
            deep[to[i]]=deep[x]+1;
            fa[to[i]]=x;
            dfs1(to[i]);
            size[x]+=size[to[i]];
        }
}

void dfs2(int x,int chain)
{
    int k=0,i;
    id[x]=++dfn;
    top[x]=chain;
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && size[to[i]]>size[k]) k=to[i];
    if (!k) return;
    dfs2(k,chain);
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && to[i]!=k) dfs2(to[i],to[i]);
}

void push_down(int cur)
{
    sum[cur<<1]+=sum[cur];
    sum[cur<<1|1]+=sum[cur];
    sum[cur]=0;
}

void ADD(int L,int R,int l,int r,int cur)
{
    if (l<=L && r>=R)
    {
        sum[cur]++;
        return;
    }
    push_down(cur);
    int mid=L+R>>1;
    if (l>mid) ADD(mid+1,R,l,r,cur<<1|1);
    else if (r<=mid) ADD(L,mid,l,r,cur<<1);
         else ADD(L,mid,l,mid,cur<<1),ADD(mid+1,R,mid+1,r,cur<<1|1);
}

int ask(int l,int r,int x,int cur)
{
    if (l==r) return sum[cur];
    push_down(cur);
    int mid=l+r>>1;
    if (x>mid) return sum[cur]+ask(mid+1,r,x,cur<<1|1);
    else return sum[cur]+ask(l,mid,x,cur<<1);
}

void Add(int x,int y)
{
    for (;top[x]!=top[y];x=fa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        ADD(1,n,id[top[x]],id[x],1);
    }
    if (deep[x]<deep[y]) swap(x,y);
    ADD(1,n,id[y],id[x],1);
}

int main()
{
    int i,x,y;
    scanf("%d",&n);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    for (i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs1(1);
    dfs2(1,1);
    for (i=1;i<n;i++) Add(a[i],a[i+1]);
    for (i=1;i<=n;i++)
    {
        ans[i]=-(i!=a[1]);
        ans[i]+=ask(1,n,id[i],1);
    }
    for (i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。