【BZOJ 3631】 [JLOI2014]松鼠的新家

3631: [JLOI2014]松鼠的新家

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 681 Solved: 329
[Submit][Status][Discuss]
Description

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。
可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。
现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。
Input

第一行一个整数n,表示房间个数
第二行n个整数,依次描述a1-an
接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。
Output

一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。
Sample Input

5

1 4 5 3 2

1 2

2 4

2 3

4 5

Sample Output

1

2

1

2

1

HINT

2<= n <=300000

树链剖分。

这道题本来是裸的树链剖分,但是我不想写这个,就用了一种奇怪的办法,写完之后发现比链剖还长。。

首先求出这棵树的欧拉序,按照欧拉序建出线段树。

每个点都在欧拉序中出现两次,我们认为第一次出现是正的,第二次是负的。

要处理 a[i1]a[i] 的路径时,先求出 lca=x ,分两种情况:
x a[i1] a[i] 的祖先,我们可以发现 a[i1] 到a[i]的路径就是欧拉序中连续的一段(两者在欧拉序中第一次出现的位置之间),我们直接在线段树的这个区间 +1 即可。

②我们需要先从 a[i1] 走到 x ,这就是①的情况;
再从 x 走到 a[i] ,这时我们需要倍增找到 a[i1] 位于 x 的哪个儿子下面(a1),然后就可以发现 x a[i] 的路径就是在a1第二次出现位置 +1 a[i] 第一次出现的位置(中间无用的点都抵消了),那么我们把线段树中这段区间 +1 即可。

其实和链剖有些像。。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cmath>
#define M 600005
using namespace std;
struct Segtree
{
    int l,r,v;
}t[M*4];
struct edge
{
    int y,ne;
}e[M];
int o[M/2][3],tot,now,dep[M],f[M/2][20],h[M],a[M],ans[M],n;
void read(int &tmp)
{
    tmp=0;
    char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar());
    for (;ch>='0'&&ch<='9';ch=getchar())
        tmp=tmp*10+ch-'0';
}
void Addedge(int x,int y)
{
    e[++tot].y=y;
    e[tot].ne=h[x];
    h[x]=tot;
}

//euler
void dfs(int x,int fa,int d)
{
    f[x][0]=fa;
    dep[x]=d;
    o[x][1]=++now;
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==fa) continue;
        dfs(y,x,d+1);
    }
    o[x][2]=++now;
}

//lca
void ST()
{
    for (int j=1;j<=19;j++)
        for (int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
}
void Move(int &x,int d)
{
    for (int j=19;j>=0;j--)
        if (dep[f[x][j]]>=d)
            x=f[x][j];
}
int Getlca(int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    if (dep[x]!=dep[y])
        Move(y,dep[x]);
    if (x==y) return x;
    for (int i=19;i>=0;i--)
        if (f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}

//segtree
void Build(int x,int l,int r)
{
    t[x].l=l,t[x].r=r,t[x].v=0;
    if (l==r) return;
    int m=(l+r)>>1;
    Build(x<<1,l,m);
    Build((x<<1)+1,m+1,r);
}
void Push_down(int x)
{
    t[x<<1].v+=t[x].v,t[(x<<1)+1].v+=t[x].v;
    t[x].v=0;
}
void Add(int x,int l,int r)
{
    if (l>r) return;
    if (t[x].l>=l&&t[x].r<=r)
    {
        t[x].v++;
        return;
    }
    if (t[x].v) Push_down(x);
    int m=(t[x].l+t[x].r)>>1;
    if (l<=m) Add(x<<1,l,r);
    if (r>m) Add((x<<1)+1,l,r);
}
void Down(int x)
{
    if (t[x].l==t[x].r)
    {
        ans[t[x].l]+=t[x].v;
        return;
    }
    if (t[x].v) Push_down(x);
    Down(x<<1),Down((x<<1)+1);
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        read(a[i]);
    for (int i=1;i<n;i++)
    {
        int x,y;
        read(x),read(y);
        Addedge(x,y),Addedge(y,x);
    }
    now=0;
    dfs(1,0,1);
    ST();
    Build(1,1,2*n);
    for (int i=2;i<=n;i++)
    {
        int x=Getlca(a[i-1],a[i]);
        int a1=a[i-1],a2=a[i];
        if (o[a1][1]>o[a2][1]) swap(a1,a2);
        if (a1==x)
        {
            Add(1,o[a1][1],o[a2][1]);
            continue;
        }
        Add(1,o[x][1],o[a1][1]);
        for (int j=19;j>=0;j--)
            if (dep[f[a1][j]]>=dep[x]+1)
                a1=f[a1][j];
        Add(1,o[a1][2]+1,o[a2][1]);
    }
    Down(1);
    for (int i=2;i<=n;i++)
        ans[o[a[i]][2]]++;
    for (int i=1;i<=n;i++)
    {
        int k=ans[o[i][1]]-ans[o[i][2]];
        printf("%d\n",k);
    }
    return 0;
}

这里写图片描述

你可能感兴趣的:(树链剖分,OI,bzoj,欧拉序)