zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    TIDB-存储
    MySQL的ACID
    MySQL MVCC
    MySQL 悲观锁、乐观锁、MVCC一
    MySQL写放大总结
    基于Linux上的wifi密码爆破
    Stack与Queue的实现(c++模板实现)
    vector 实现二维数组
    Linux下的静态链接与动态链接
    260. Single Number III
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9033984.html
Copyright © 2011-2022 走看看