树上差分(点覆盖/边覆盖)

树上差分

树上差分实际上类似于树链剖分,将一条链分为轻链和重链,再分别对两个直链去做差分标记,最后再去对于每个点dfs他的子节点求树上前缀和.

类似的题目自己做过的不多,这里推荐两道比较简单的题目,都是边覆盖问题.

EOJ Monthly 2018.8 D. Delivery Service|| P2680 运输计划

树上边差分(边覆盖)

struct Point_pair
{
    int len,lca,u,v;
}pp[maxn];

inline int dfs(int u,int pre)
{
    int ans=s[u];
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].to;
        if(v==pre) continue;
        ans+=dfs(v,u);
    }
    tot[++pos]=ans;
    return ans;
}
int cal(int n,int m)//边覆盖最小/最大权
{
    memset(s,0,sizeof(s));
    for(int i=1; i<=m; i++)//树上边差分
    {
        s[pp[i].u]++;
        s[pp[i].v]++;
        s[pp[i].lca]-=2;
    }
    dfs(1,0);
    int ans=0;
    sort(w+1,w+n);
    sort(tot+1,tot+pos,cmp);
    for(int i=1;i<pos;i++) ans+=w[i]*tot[i];
    return ans;
}

树上点差分(点覆盖)

struct Point_pair
{
    int len,lca,u,v;
}pp[maxn];

inline int dfs(int u,int pre)
{
    int ans=s[u];
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].to;
        if(v==pre) continue;
        ans+=dfs(v,u);
    }
    tot[++pos]=ans;
    return ans;
}
int cal(int n,int m)//点覆盖最小/最大权
{
    memset(s,0,sizeof(s));
    for(int i=1; i<=m; i++)//树上点差分
    {
        s[pp[i].u]++;
        s[pp[i].v]++;
        s[pp[i].lca]--;
        s[f[pp[i].lca]]--;
    }
    dfs(1,0);
    int ans=0;
    sort(w+1,w+n);
    sort(tot+1,tot+pos,cmp);
    for(int i=1;i<pos;i++) ans+=w[i]*tot[i];
    return ans;
}

猜你喜欢

转载自blog.csdn.net/Murphyc/article/details/81671064
今日推荐