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
1 4 5 3 2
1 2
2 4
2 3
4 5
Sample Output
1
2
1
2
1
2
1
2
1
HINT
2<= n <=300000
题解Here!
这题有两种做法。
一、树上差分
这个不就是一道板子题?
就是:
sum[x]++;sum[fa[y]]++;
sum[LCA(x,y)]--;sum[fa[LCA(x,y)]]--;
注意,一定是sum[ fa[ y ] ] ++,因为这次到达了那个点与下次从这个点出发会计算两次。
最后一边统计即可。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 300010
using namespace std;
int n,m,c=1;
int head[MAXN<<1],deep[MAXN],f[MAXN][20],num[MAXN],s[MAXN],ans[MAXN];
struct node{
int next,to;
}a[MAXN<<1];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
void add(int x,int y){
a[c].to=y;
a[c].next=head[x];
head[x]=c++;
a[c].to=x;
a[c].next=head[y];
head[y]=c++;
}
void buildtree(int rt){
int will;
for(int i=head[rt];i;i=a[i].next){
will=a[i].to;
if(!deep[will]){
deep[will]=deep[rt]+1;
f[will][0]=rt;
buildtree(will);
}
}
}
void step(){
for(int i=1;i<=19;i++)
for(int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
}
int LCA(int x,int y){
if(deep[x]<deep[y])swap(x,y);
for(int i=19;i>=0;i--)
if(deep[f[x][i]]>=deep[y])
x=f[x][i];
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];
}
void work(int x,int y){
int fa=LCA(x,y);
s[x]++;s[f[y][0]]++;s[fa]--;
if(f[fa][0]!=0)s[f[fa][0]]--;
}
void getsum(int now,int rt){
int will;
ans[now]=s[now];
for(int i=head[now];i;i=a[i].next){
will=a[i].to;
if(will!=rt){
getsum(will,now);
ans[now]+=ans[will];
}
}
}
int main(){
int x,y;
n=read();
for(int i=1;i<=n;i++)num[i]=read();
for(int i=1;i<n;i++){
x=read();y=read();
add(x,y);
s[i]=deep[i]=0;
}
s[n]=deep[n]=0;
deep[1]=1;
buildtree(1);
step();
for(int i=1;i<n;i++)work(num[i],num[i+1]);
getsum(1,0);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}
二、树链剖分
因为从房间出发的时候不需要糖果,到达的时候才需要糖果。
所以每次开始会在出发点多放一块糖果。
那我们就update(s,s,-1),把多放的那块减去。
第一个房间是起点,提前在for循环外边放上一块糖。
最后让终点的糖--,因为题目中说最后一个点是餐厅,不用放糖。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) b[x].data
#define SIGN(x) b[x].c
#define LSIDE(x) b[x].l
#define RSIDE(x) b[x].r
#define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
#define MAXN 300010
using namespace std;
int n,m,c=1,d=1;
int num[MAXN],head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
struct node1{
int next,to;
}a[MAXN<<1];
struct node2{
int data,c,l,r;
}b[MAXN<<2];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void add(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs1(int rt){
son[rt]=0;size[rt]=1;
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(!deep[will]){
deep[will]=deep[rt]+1;
fa[will]=rt;
dfs1(will);
size[rt]+=size[will];
if(size[son[rt]]<size[will])son[rt]=will;
}
}
}
void dfs2(int rt,int f){
id[rt]=d++;top[rt]=f;
if(son[rt])dfs2(son[rt],f);
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(will!=fa[rt]&&will!=son[rt])
dfs2(will,will);
}
}
inline void pushup(int rt){
DATA(rt)=DATA(LSON)+DATA(RSON);
}
inline void pushdown(int rt){
if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
SIGN(LSON)+=SIGN(rt);
DATA(LSON)+=SIGN(rt)*WIDTH(LSON);
SIGN(RSON)+=SIGN(rt);
DATA(RSON)+=SIGN(rt)*WIDTH(RSON);
SIGN(rt)=0;
}
void buildtree(int l,int r,int rt){
int mid;
LSIDE(rt)=l;
RSIDE(rt)=r;
if(l==r){
DATA(rt)=0;
return;
}
mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
pushup(rt);
}
void update(int l,int r,int c,int rt){
int mid;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
SIGN(rt)+=c;
DATA(rt)+=c*WIDTH(rt);
return;
}
pushdown(rt);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)update(l,r,c,LSON);
if(mid<r)update(l,r,c,RSON);
pushup(rt);
}
int query(int l,int r,int rt){
int mid,ans=0;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
pushdown(rt);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)ans+=query(l,r,LSON);
if(mid<r)ans+=query(l,r,RSON);
return ans;
}
void work_add(int x,int y,int k){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
update(id[top[x]],id[x],k,1);
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
update(id[x],id[y],k,1);
}
void work(){
work_add(num[1],num[1],1);
for(int i=1;i<n;i++){
work_add(num[i],num[i+1],1);
update(id[num[i]],id[num[i]],-1,1);
}
update(id[num[n]],id[num[n]],-1,1);
for(int i=1;i<=n;i++)printf("%d\n",query(id[i],id[i],1));
}
void init(){
int x,y;
n=read();
for(int i=1;i<=n;i++)num[i]=read();
for(int i=1;i<n;i++){
x=read();y=read();
add(x,y);
}
deep[1]=1;
dfs1(1);
dfs2(1,1);
buildtree(1,n,1);
}
int main(){
init();
work();
return 0;
}