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[i−1]−a[i] 的路径时,先求出 lca=x ,分两种情况:
① x 为 a[i−1] 或 a[i] 的祖先,我们可以发现 a[i−1] 到a[i]的路径就是欧拉序中连续的一段(两者在欧拉序中第一次出现的位置之间),我们直接在线段树的这个区间 +1 即可。
②我们需要先从 a[i−1] 走到 x ,这就是①的情况;
再从 x 走到 a[i] ,这时我们需要倍增找到 a[i−1] 位于 x 的哪个儿子下面(a1),然后就可以发现 x 到 a[i] 的路径就是在a1第二次出现位置 +1 到 a[i] 第一次出现的位置(中间无用的点都抵消了),那么我们把线段树中这段区间 +1 即可。
其实和链剖有些像。。
#include
#include
#include
#include
#include
#include
#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;iint 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;
}