P3258 松鼠的新家

#$Description$ [题面](https://www.luogu.org/problem/P3258) 给你一张$n$个节点的树和一个遍历的顺序,必须按照顺序走,求每个点被经过多少次,最后到达的点不计算次数 #$Solution$ 树上差分的裸题,只需要做点差分就行了。 对于路径上相邻两点做点差分会发现有的点被统计两次,所以对于第$2-(n-1)$个经过的点标记一下,最后$dfs$的时候先让被标记的点$ans[i]=cf[i],ans[i]--$即可,为什么不让$cf[i]--$?因为我们计算的$cf[i]$一定是合法的,只有中转点因为统计两次,所以继续传下去是对的,最后别忘了让最后到达的点$ans--$,因为最后的点不计算次数 #$Code$ ``` #include #include #include #include #define re register #define maxn 300010 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct Edge{ int v,nxt; }e[maxn<<2]; int a[maxn],x,y,fat[maxn],vis[maxn]; int cf[maxn],ans[maxn],tmp,dep[maxn],head[maxn],cnt; int n,m,lg[maxn],num,f[maxn][23]; inline void add(int u,int v) { e[++cnt].v=v; e[cnt].nxt=head[u]; head[u]=cnt; } void pre() { for(re int i=1,num=0;i<=n;i*=2) lg[i]=num++; for(re int i=1;i<=n;++i) if(!lg[i]) lg[i]=lg[i-1]; } void dfs(int u,int fa) { fat[u]=fa; dep[u]=dep[fa]+1; f[u][0]=fa; for(int i=1;(1<=0;--i) { if(dep[f[x][i]]=0;--i) { if(f[x][i]==f[y][i]) continue; x=f[x][i],y=f[y][i]; } return f[x][0]; } void dfs2(int u,int fa) { for(int i=head[u];i;i=e[i].nxt) { int ev=e[i].v;//一个坑,注意别定义全局变量 if(ev==fa) continue; dfs2(ev,u); cf[u]+=cf[ev]; } ans[u]=cf[u]; if(vis[u]) ans[u]--;

}
int main()
{
n=read();
for(re int i=1;i<=n;++i) a[i]=read();
for(re int i=1;i<n;++i)
{
x=read(),y=read();
add(x,y);
add(y,x);
}
pre();
dfs(1,0);
for(re int i=1;i<n;++i)
{
tmp=lca(a[i],a[i+1]);
if(i!=n-1) vis[a[i+1]]=1;
cf[a[i]]++;
cf[a[i+1]]++;
cf[tmp]--;
cf[fat[tmp]]--;
}
dfs2(1,0);
ans[a[n]]--;//最后经过的点不需要计算
for(re int i=1;i<=n;++i) printf("%d ",ans[i]);
return 0;
}

</font>
原文地址:https://www.cnblogs.com/Liuz8848/p/11673503.html