洛谷P3258 [JLOI2014]松鼠的新家——题解

题目传送门
题目大意:
给出一个在树上移动的序列,求出每个点被经过的次数。


思考过程:
维护两个点之间的链的信息很明显我们需要树剖,但是树剖无法维护链上每个节点的信息,所以我们需要借助差分数组。


具体做法:
1.树剖
2.对于序列中相邻的两个点,像普通树剖求答案那样往上跳到LCA,过程中将两个节点的差分数组起点+1,终点-1,注意重复的情况,用tag数组来记录


代码:

#include 
using namespace std;

const int maxn=3e5+1000;
struct stu
{
    int to,next;
}road[maxn*2]; int first[maxn],cnt=0;
int dep[maxn],cnum[maxn],id[maxn],dfn[maxn],top[maxn],fa[maxn],hson[maxn],cf[maxn],sum[maxn],tag[maxn],a[maxn];
int n,nowtop;

void addedge(int x,int y)
{
    road[++cnt].to=y;
    road[cnt].next=first[x];
    first[x]=cnt;
}

void dfs1(int now,int depth)
{
    int maxx=0;
    dep[now]=depth;
    cnum[now]=1;
    for(int i=first[now];i;i=road[i].next)
    {
        int to=road[i].to;
        if(to==fa[now]) continue;
        fa[to]=now;
        dfs1(to,depth+1);
        cnum[now]+=cnum[to];
        if(cnum[to]>maxx)
        {
            maxx=cnum[to];
            hson[now]=to;
        }
    }
}

void dfs2(int now,bool what)
{
    if(what==1) nowtop=now;
    top[now]=nowtop;
    id[now]=++cnt;
    dfn[cnt]=now;
    if(!hson[now]) return;
    dfs2(hson[now],0);
    for(int i=first[now];i;i=road[i].next)
    {
        int to=road[i].to;
        if(to==fa[now]||to==hson[now]) continue;
        dfs2(to,1);
    }
}

void calcu(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]if(dep[x]int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);addedge(y,x);
    }
    dfs1(a[1],1);
    cnt=0;
    dfs2(a[1],1);
    calcu(a[1],a[2]);
    for(int i=2;i<=n-1;i++)
    {
        calcu(a[i],a[i+1]);
        tag[a[i]]--;
    }
    tag[a[n]]--;
    sum[1]=cf[1];
    for(int i=2;i<=n;i++) sum[i]=sum[i-1]+cf[i];
    for(int i=1;i<=n;i++) printf("%d\n",sum[id[i]]+tag[i]);
    return 0;
}

你可能感兴趣的:(题解)