【bzoj3631】[JLOI2014]松鼠的新家
题目大意就是按照他给的顺序遍历树,问每个节点会经过几次,最后的那个要减1
类似前缀和的思想
从起点x到终点y,只需给x,y两个结点加1,给LCA(x,y),fa[LCA(x,y)]减1,最后做一次从底到根的递推即可求出每个点在多少条链上
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; typedef long long LL; #define N 300010 struct edge { int to,next; }e[N<<2]; int head[N<<2]; int cnt; int n; int id; int x,y; int a[N],ans[N]; int siz[N],dep[N],fa[N],top[N],son[N],dfn[N]; inline int getint() { int x=0,f=1;char ch=getchar(); while (ch>'9' || ch<'0') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void link(int x,int y) { e[++cnt]=(edge){y,head[x]}; head[x]=cnt; } void dfs(int x) { siz[x]=1; son[x]=0; dfn[++id]=x; for (int i=head[x];i;i=e[i].next) { int v=e[i].to; if (fa[x]!=v) { fa[v]=x; dep[v]=dep[x]+1; dfs(v); siz[x]+=siz[v]; if (siz[v]>siz[son[x]]) son[x]=v; } } } void dfs2(int x,int d) { top[x]=d; if (son[x]) dfs2(son[x],d); for (int i=head[x];i;i=e[i].next) { int v=e[i].to; if (fa[x]!=v && son[x]!=v) dfs2(v,v); } } int lca(int x,int y) { while (top[x]!=top[y]) { int a=top[x]; int b=top[y]; if (dep[a]<dep[b]) swap(a,b),swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y] ? x : y; } int main() { n=getint(); for (int i=1;i<=n;i++) a[i]=getint(); for (int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); } dfs(1); dfs2(1,1); for (int i=2;i<=n;i++) { int u=a[i-1],v=a[i],w=lca(u,v); ans[u]++; ans[v]++; ans[w]--; ans[fa[w]]--; } for (int i=n;i>=1;i--) ans[fa[dfn[i]]]+=ans[dfn[i]]; for (int i=1;i<=n;i++) if (a[1]!=i) printf("%d\n",ans[i]-1); else printf("%d\n",ans[i]); return 0; }