1. 程式人生 > >bzoj 3631 松鼠的新家 (樹鏈剖分)

bzoj 3631 松鼠的新家 (樹鏈剖分)

連結: https://www.lydsy.com/JudgeOnline/problem.php?id=3631

思路:

直接用樹鏈剖分求每一次運動,因為這道題只需要區間增添,單點求值,沒必要用線段樹,直接陣列標記下就好了

 

實現程式碼

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid int m = (l + r) >> 1

const
int M = 1e5 + 10; int cnt,cnt1; int head[M],dep[M],fa[M],top[M],son[M],siz[M],tid[M],sum[M],a[M]; struct node{ int to,next; }e[M]; void add(int u,int v){ e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt; } void dfs1(int u,int faz,int deep){ dep[u] = deep; fa[u] = faz; siz[u]
= 1; for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v == fa[u]) continue; dfs1(v,u,deep+1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]||son[u] == -1) son[u] = v; } } void dfs2(int u,int t){ top[u] = t; tid[u]
= ++cnt1; if(son[u] == -1) return ; dfs2(son[u],t); for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v != fa[u]&&v != son[u]) dfs2(v,v); } } void update(int x,int y){ sum[x]++; sum[y+1]--; } void solve(int x,int y){ int fx = top[x],fy = top[y]; int ans = 0; while(fx!=fy){ if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy); update(tid[fx],tid[x]); x = fa[fx]; fx = top[x]; } if(dep[x] > dep[y]) swap(x,y); update(tid[x],tid[y]); } int main(){ int n,x,y; scanf("%d",&n); memset(son,-1,sizeof(son)); for(int i = 1;i <= n;i ++){ scanf("%d",&a[i]); } int n1 = n-1; while(n1--){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1,0,1); dfs2(1,0); for(int i = 2;i <= n;i ++) solve(a[i-1],a[i]); for(int i = 1;i <= n;i ++){ sum[i] += sum[i-1]; } cout<<1<<endl; for(int i = 1;i <= n;i ++){ if(i == 1) printf("%d\n",sum[tid[i]]); else printf("%d\n",sum[tid[i]]-1); } return 0; }