「JLOI 2014」松鼠的新家

传送门


problem

前几天松鼠刚刚装修了新家,新家有 n n n 个房间,并且有 n − 1 n-1 n1 根树枝连接,每个房间都可以相互到达,且任意两个房间之间的路线都是唯一的。

松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去 a 1 a_1 a1,再去 a 2 a_2 a2 ⋯ ⋯ \cdots\cdots ,最后到 a n a_n an,去参观新家。

可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间 a n a_n an 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

数据范围: 2 ≤ n ≤ 3 × 1 0 5 2 ≤ n ≤ 3\times 10^5 2n3×105


solution

直接树链剖分,然后树上差分做就可以了。

注意一下有些重复加的细节问题。


code

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=3e5+5;
int n,a[N];
int t,first[N],v[N<<1],nxt[N<<1];
void add(int x,int y){
	nxt[++t]=first[x],first[x]=t,v[t]=y;
}
int tot,fa[N],dep[N],sze[N],son[N],top[N],pos[N],idx[N];
void dfs1(int x){
	sze[x]=1;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(to==fa[x])  continue;
		fa[to]=x,dep[to]=dep[x]+1;
		dfs1(to),sze[x]+=sze[to];
		if(sze[son[x]]<sze[to])  son[x]=to;
	}
}
void dfs2(int x,int tp){
	top[x]=tp,idx[pos[x]=++tot]=x;
	if(son[x])  dfs2(son[x],tp);
	for(int i=first[x];i;i=nxt[i])
		if(v[i]!=fa[x]&&v[i]!=son[x])  dfs2(v[i],v[i]);
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])  swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
int Tag[N];
int main(){
	n=in();
	for(int i=1;i<=n;++i)  a[i]=in();
	for(int i=1,x,y;i<n;++i){
		x=in(),y=in(),add(x,y),add(y,x);
	}
	dfs1(1),dfs2(1,1);
	for(int i=1;i<n;++i){
		int u=a[i],v=a[i+1],lca=LCA(u,v);
		Tag[u]++,Tag[v]++,Tag[lca]--,Tag[fa[lca]]--;
	}
	for(int i=n;i>=1;--i)  Tag[fa[idx[i]]]+=Tag[idx[i]];
	for(int i=1;i<=n;++i)  printf("%d\n",Tag[i]-1+(i==a[1]));
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值