您的位置:首页 > 其它

[bzoj 3631--JLOI2014]松鼠的新家

2018-03-22 10:52 603 查看
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。

可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。

现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

这道题用树剖直接可以做,但是想用一个代码复杂度小的方法。如果学过树上差分的同学就会知道,这道题是一种基本思路,对点进行修改。修改操作时只需让s[l]++,s[r]++,s[lca]–,s[f[lca][0]]–,询问操作时,只需算出它的总子树的s和。为什么呢,%一下

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
int x,y,next;
}a[650010];int len,last[350010];
void ins(int x,int y)
{
len++;
a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
long long f[350010][22],dep[350010],sx[350010];
long long ss[350010];
void dfs1(int x)
{
for(int i=1;i<=19;i++)f[x][i]=f[f[x][i-1]][i-1];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[x][0])
{
f[y][0]=x;
dep[y]=dep[x]+1;
dfs1(y);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)
{
if((dep[x]-dep[y])>=(1<<i))x=f[x][i];
}
if(x==y)return x;
for(int i=19;i>=0;i--)
{
if(dep[x]>=(1<<i) && f[x][i]!=f[y][i])
{
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
void dfs2(int x)
{
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[x][0])
{
dfs2(y);
ss[x]+=ss[y];
}
}
}
int main()
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&sx[i]);
for(int i=1;i<n;i++)
{
long long x,y;
scanf("%lld%lld",&x,&y);
ins(x,y);ins(y,x);
}
dfs1(sx[1]);
for(int i=1;i<n;i++)
{
int x=sx[i],y=sx[i+1];
ss[x]++;ss[y]++;
int lca=LCA(x,y);
ss[lca]--;ss[f[lca][0]]--;
}
dfs2(sx[1]);
for(int i=2;i<=n;i++)ss[sx[i]]--;
for(int i=1;i<=n;i++)printf("%lld\n",ss[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj JLOI 树上差分