P3258 松鼠的新家

\(Description\)

题面
给你一张\(n\)个节点的树和一个遍历的顺序,必须按照顺序走,求每个点被经过多少次,最后到达的点不计算次数

\(Solution\)

树上差分的裸题,只需要做点差分就行了。
对于路径上相邻两点做点差分会发现有的点被统计两次,所以对于第\(2-(n-1)\)个经过的点标记一下,最后\(dfs\)的时候先让被标记的点\(ans[i]=cf[i],ans[i]--\)即可,为什么不让\(cf[i]--\)?因为我们计算的\(cf[i]\)一定是合法的,只有中转点因为统计两次,所以继续传下去是对的,最后别忘了让最后到达的点\(ans--\),因为最后的点不计算次数

\(Code\)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#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<<i)<=dep[u];++i)
     f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int ev=e[i].v;
        if(ev!=fa) dfs(ev,u);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(re int i=lg[dep[x]-dep[y]];i>=0;--i)
    {
        if(dep[f[x][i]]<dep[y]) continue;
        x=f[x][i];
    }
    if(x==y) return x;
    for(re int i=lg[dep[x]-1];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\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Liuz8848/p/11673503.html