树上差分
树上差分实际上类似于树链剖分,将一条链分为轻链和重链,再分别对两个直链去做差分标记,最后再去对于每个点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;
}