zoukankan      html  css  js  c++  java
  • BZOJ 3631 【JLOI2014】 松鼠的新家

    Description

    松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。
    可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。
    现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

    Input

    第一行一个整数n,表示房间个数
    第二行n个整数,依次描述a1-an
    接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

    Output

    一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

    HINT

    2<= n <=300000

      这道题一看,这不是要求每次给树上的一条路径权值加一,最后询问每个点的权值。那么这就是树剖的板子题了,套个树状数组即可。

      然而,由于这道题只在最后有一个询问操作,那么树链剖分未免有点大材小用了。我们可以维护一个数组$b$,每次给$b_i$加上$x$就表示给从$i$到根的路径上所有节点的权值都加上$x$。这样,我们只需在最后$dfs$一遍,把值自底往上加即可得到权值数组。这样的话,每次给路径$(u,v)$上的点全部加一对应需要改变$b$数组中四个值:$b_u+1$,$b_v+1$,$lca(u,v)-1$,$fa(lca(u,v))-1$即可。这样我们修改的复杂度就极大地降低了。

      然后,我一开始写了一个倍增求$lca$,后来发现其他人都跑得比我快……自己想了想,还是决定写个$Tarjan$求$lca$算了,毕竟以前没有写过……后来发现这真的好写,不比倍增难写……

      $Tarjan$求$lca$大概的思路就是在$dfs$的时候,维护一个并查集,对已经$dfs$完了的点在并查集中指向它的$father$,正在栈中的点指向它自己。这样的话,每到一个点$u$,我们可以扫一遍所有的$(u,v)$这种询问,如果$v$被访问过了,那么这个询问的答案就是并查集中$v$的树根。感觉这个自己$YY$一下不难证明,这里就不证明了。

      下面贴代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    #define maxn 300010
    #define maxm 600010
    
    using namespace std;
    typedef long long llg;
    
    struct data{
    	int u,v;
    }s[maxm];
    int head[maxn],next[maxm],to[maxm],tt;
    int he[maxn],ne[maxm],t1[maxm],t2;
    int n,ans[maxn],fa[maxn],a[maxn],ff[maxn],b[maxn];
    
    int getint(){
    	int w=0;bool q=0;
    	char c=getchar();
    	while((c>'9'||c<'0')&&c!='-') c=getchar();
    	if(c=='-') c=getchar(),q=1;
    	while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
    	return q?-w:w;
    }
    
    int find(int x){return fa[fa[x]]==fa[x]?fa[x]:fa[x]=find(fa[x]);}
    void lik(int x,int y){t1[++t2]=y;ne[t2]=he[x];he[x]=t2;}
    void link(int x,int y){
    	to[++tt]=y;next[tt]=head[x];head[x]=tt;
    	to[++tt]=x;next[tt]=head[y];head[y]=tt;
    }
    
    void dfs(int u,int f){
    	fa[u]=u; ff[u]=f;
    	for(int i=he[u],v;v=t1[i],i;i=ne[i])
    		if(fa[s[v].v]) ans[(v-1)%(n-1)+1]=find(s[v].v);
    	for(int i=head[u],v;v=to[i],i;i=next[i])
    		if(v!=f) dfs(v,u);
    	fa[u]=f;
    }
    
    void dfs(int u){
    	for(int i=head[u],v;v=to[i],i;i=next[i])
    		if(v!=ff[u]) dfs(v),b[u]+=b[v];
    }
    
    int main(){
    	File("a");
    	n=getint();
    	for(int i=1;i<=n;i++) a[i]=getint();
    	for(int i=1;i<n;i++){
    		s[i].u=s[i+n-1].v=a[i]; lik(s[i].u,i);
    		s[i].v=s[i+n-1].u=a[i+1]; lik(s[i].v,i+n-1);
    	}
    	for(int i=2;i<=n;i++) link(getint(),getint());
    	dfs(1,0);
    	for(int i=1;i<n;i++){
    		b[s[i].u]++; b[ff[s[i].v]]++;
    		b[ans[i]]--; b[ff[ans[i]]]--;
    	}
    	dfs(1);
    	for(int i=1;i<=n;i++) printf("%d
    ",b[i]);
    	return 0;
    }
  • 相关阅读:
    餐巾计划问题 zwk费用流解法
    Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵
    smarty之缓存机制
    mysql中 where in 用法详解
    sql语句中left join、inner join中的on与where的区别
    PHP表单数组的具体使用方法介绍
    document.body.scrollTop值为0的解决方法[转]
    left join on and和left join on where条件的困惑[转]
    Uedit32_17.00 修改某一语言背景色-修改后续名后语法着色及某语言的大括号{}对齐
    CSS 针对谷歌浏览器(Chrome) safari的webkit核心浏览器CSS hack
  • 原文地址:https://www.cnblogs.com/lcf-2000/p/6063211.html
Copyright © 2011-2022 走看看