BZOJ3631: [JLOI2014]松鼠的新家

BZOJ3631: [JLOI2014]松鼠的新家

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

题解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
",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
",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;
}
原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9033984.html