P3258 [JLOI2014]松鼠的新家

传送门

此题树剖可过

然而可以树上差分为什么要树剖..

对于一条路径(A,B),只要把 val [ A ] ++ , val [ B ] ++ , val [ LCA(A,B) ] -- , val [ fa[LCA(A,B)] ] --

那么求每个点的经过次数就求一下子树 val 的和就好了

但是要注意,每一条路径的终点是下一条路径的起点,那些点会被多算一次

所以求完子树和以后要再把那些点 val--

代码很简单

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N=6e5+7;
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<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}

int fir[N],from[N<<1],to[N<<1],cnt;
inline void add(int &a,int &b)
{
    from[++cnt]=fir[a];
    fir[a]=cnt; to[cnt]=b;
}

int n;

int dep[N],f[N][27];//LCA的数组
void dfs1(int x,int fa)//dfs1预处理f和dep
{
    f[x][0]=fa; dep[x]=dep[fa]+1;
    for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==fa) continue;
        dfs1(v,x);
    }
}
inline int LCA(int x,int y)//求LCA
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

int a[N],sum[N];
void dfs2(int x)//dfs2求子树和
{
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==f[x][0]) continue;
        dfs2(v); sum[x]+=sum[v];
    }
}
int main()
{
    int b,c;
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++) b=read(),c=read(),add(b,c),add(c,b);
    dfs1(1,1);
    int t;
    for(int i=1;i<n;i++)
    {
        sum[a[i]]++; sum[a[i+1]]++;
        t=LCA(a[i],a[i+1]);
        sum[t]--; if(t!=1) sum[f[t][0]]--;//注意细节
    }
    dfs2(1);
    for(int i=2;i<=n;i++) sum[a[i]]--;//减去重复算的
    for(int i=1;i<=n;i++)
        printf("%d\n",sum[i]);
    return 0;
}

 

posted @ 2018-10-15 12:54  LLTYYC  阅读(222)  评论(0编辑  收藏  举报