[ JLOI 2014 ] 松鼠的新家

\(\\\)

\(Description\)


给出一棵\(N​\)个节点的树,按顺序依次访问\(N​\)个节点\(A_1,...,A_N​\),即在树上走每一条\(A_i​\)\(A_{i+1}​\)到最短路。

注意,除了第一次直接从起点出发,以后每一次起点都是上一次的终点,且在这两次移动中视为该点只访问一次。

现要求每次访问过的点权值都加一,求最后树上每一个节点的权值。

  • \(N\in [1,3\times 10^5]\)

\(\\\)

\(Solution\)


  • 树上差分板子。对于每个\(i\),在\(A_i\)\(A_{i+1}\)处打\(+1\)标记,在\(Lca(A_i,A_{i+1})\)处打\(-1\)标记,在\(father\big(Lca(A_i,A_{i+1})\big)\)处打\(-1\)标记即可\((\)其中\(father(i)\)表示\(i\)的直接父亲,根节点返回值为\(0\)\()\)

  • 这种做法的合理性在于,我们需要将路径上经过的所有点都打上一次标记,而统计子树和以更新当前节点权值时,这个影响并不会上传到\(father\big(Lca(A_i,A_{i+1})\big)\)处,所以需要在\(father\big(Lca(A_i,A_{i+1})\big)\)处抵消掉所有影响。而只在\(father\big(Lca(A_i,A_{i+1})\big)\)处抵消影响时不够的,因为在两点\(Lca\)处本次增加的子树和会被累计两次,所以需要在\(Lca\)处先\(-1\),再在\(father\big(Lca(A_i,A_{i+1})\big)\)\(-1\)

  • 各点点权可以一遍\(DFS\)求出子树和后更新。注意题目要求每次直接访问的节点只算一次,所以最后在这些节点处权值\(-1\),注意第一个访问到的点并不需要\(-1\),还要注意这个操作一定要在\(DFS\)求子树和之后做

\(\\\)

\(Code\)


#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300010
#define R register
#define gc getchar
using namespace std;
 
inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}
 
int n,t,tot,d[N],hd[N],num[N],cnt[N],f[N][20];
struct edge{int to,nxt;}e[N<<1];
inline void add(int u,int v){
  e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
}
 
queue<int> q;
inline void bfs(){
  q.push(1); d[1]=1;
  while(!q.empty()){
    int u=q.front(); q.pop();
    for(R int i=hd[u],v;i;i=e[i].nxt)
      if(!d[v=e[i].to]){
        d[v]=d[u]+1; f[v][0]=u;
        for(R int i=1;i<=t;++i) f[v][i]=f[f[v][i-1]][i-1];
        q.push(v);
      }
  }
}
 
inline int lca(int u,int v){
  if(d[u]>d[v]) u^=v^=u^=v;
  for(R int i=t;~i;--i) if(d[f[v][i]]>=d[u]) v=f[v][i];
  if(u==v) return u;
  for(R int i=t;~i;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
  return f[u][0];
}
 
inline void dfs(int u,int fa){
    for(R int i=hd[u],v;i;i=e[i].nxt)
        if((v=e[i].to)!=fa){
            dfs(v,u); cnt[u]+=cnt[v];
        }
}
 
int main(){
  t=log2(n=rd());
  for(R int i=1;i<=n;++i) num[i]=rd();
  for(R int i=1,u,v;i<n;++i){
    u=rd(); v=rd(); add(u,v); add(v,u);
  }
  bfs();
  for(R int i=1;i<n;++i){
    int l=lca(num[i],num[i+1]);
    ++cnt[num[i]]; ++cnt[num[i+1]];
    --cnt[l]; --cnt[f[l][0]];
  }
  dfs(1,0);
  for(R int i=2;i<=n;++i) --cnt[num[i]];
  for(R int i=1;i<=n;++i) printf("%d\n",cnt[i]);
  return 0;
}
posted @ 2018-09-18 21:53  SGCollin  阅读(125)  评论(0编辑  收藏  举报