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
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; }