1. 程式人生 > >2018.11.09【NOIP2016】【洛谷P1600】天天愛跑步(樹上差分)

2018.11.09【NOIP2016】【洛谷P1600】天天愛跑步(樹上差分)

傳送門

解析:

據說這是NOIP歷年最難一道題。。但是真的沒有寶藏難啊我覺得。。。

思路:

答案分兩類統計,一種是子樹中過來,一種是其他地方過來。那麼路徑就被拆分成兩部分了,一部分是S>lcaS->lca,一部分是lca>Tlca->T,兩部分分別對應兩種答案的統計,這就可以樹上差分了。

第一種:從子樹中經過。設統計答案的點為uu
子樹中為起點則必須要滿足depu+wu=depSdep_u+w_u=dep_S,即統計子樹內有多少個深度為depu+wudep_u+w_u

的點,這個就是樹上差分的基礎應用了。

第二種:從其他子樹來。設統計答案的點為uu
則需要記錄入答案的路徑需要滿足LCA(u,S)==LCA(S,T)LCA(u,S)==LCA(S,T),不過這個不需要體現在程式碼上。而滿足路徑長度則需要wudepu=deplca×2depSw_u-dep_u=dep_{lca}\times 2-dep_S
這個等式左邊只和uu有關,右邊和uu沒有關係,同樣樹上差分解決。

程式碼:

#include<bits/stdc++.h>
using
namespace std; #define ll long long #define re register #define gc getchar #define pc putchar #define cs const inline int getint(){ re int num; re char c; while(!isdigit(c=gc()));num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return num; } cs int N=300005; int bin1[N<<
1],bbb[N<<1]; int *cs bin2=bbb+N; int last[N],nxt[N<<1],to[N<<1],ecnt; inline void addedge(int u,int v){ nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v; nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u; } int w[N]; int fa[N],top[N],dep[N],siz[N],son[N]; inline void dfs1(int u){ siz[u]=1; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; fa[v]=u; dep[v]=dep[u]+1; dfs1(v); siz[u]+=siz[v]; if(siz[v]>siz[son[u]])son[u]=v; } } inline void dfs2(int u){ for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; if(son[u]==v)top[v]=top[u]; else top[v]=v; dfs2(v); } } inline void tree_dissection(int root=1){ dfs1(root); top[root]=root; dfs2(root); } inline int LCA(int u,int v){ while(top[u]^top[v]){ if(dep[top[u]]>dep[top[v]])swap(u,v); v=fa[top[v]]; } return dep[u]>dep[v]?v:u; } vector<int> pos1[N],pos2[N],add1[N],add2[N]; int ans[N]; inline void dfs(int u){ int pre1=bin1[dep[u]+w[u]],pre2=bin2[w[u]-dep[u]]; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; dfs(v); } for(int re i=0;i<pos1[u].size();++i)bin1[pos1[u][i]]+=add1[u][i]; for(int re i=0;i<pos2[u].size();++i)bin2[pos2[u][i]]+=add2[u][i]; ans[u]+=bin1[dep[u]+w[u]]-pre1+bin2[w[u]-dep[u]]-pre2; } int n,m; signed main(){ n=getint(); m=getint(); for(int re i=1;i<n;++i){ int u=getint(),v=getint(); addedge(u,v); } tree_dissection(); for(int re i=1;i<=n;++i) w[i]=getint(); for(int re i=1;i<=m;++i){ int u=getint(),v=getint(),lca=LCA(u,v); pos1[u].push_back(dep[u]);add1[u].push_back(1); pos1[lca].push_back(dep[u]);add1[lca].push_back(-1); pos2[v].push_back(dep[u]-2*dep[lca]);add2[v].push_back(1); pos2[fa[lca]].push_back(dep[u]-2*dep[lca]);add2[fa[lca]].push_back(-1); } dfs(1); for(int re i=1;i<=n;++i)printf("%d ",ans[i]); return 0; }