测试地址:Masha and Cactus
题目大意: 给定一棵树,再给定
m
m
m条边,每条边有权值,从里面选出一些边加入树中,使得形成的图是仙人掌,即每个点至多处在一个环中的图,并使得加入的边的权值和最大,求出这个最大值。
做法: 本题需要用到树形DP+LCA+树状数组。
首先转化一下问题。不难想到,添加一条边会使得树上的一条路径上的点被一个环覆盖,那么要使一个点至多在一个环中,也就是要求添加的边所对应的路径不相交。那么问题就转化为,从
m
m
m条路径中选出若干不相交的路径,使得它们的权值和最大。
不难想到用树形DP选择此题。令
f
(
v
)
f(v)
f(v)为以
v
v
v为根子树的答案,那么
v
v
v要么不被覆盖,这种情况答案就是它所有儿子的
f
f
f值之和,要么
v
v
v就被某条路径覆盖,而因为我们要算的是以
v
v
v为根子树的答案,所以我们只需考虑LCA为
v
v
v的路径即可。这样的话每条路径会在其两个端点的LCA处被处理,直接在DP前预处理出来即可。在选择了某一条路径后,要使得答案最大,就是要求在子树中把该路径所有点删掉,剩下的所有子树都达到最大,也就是求子树中和这条路径直接相邻的点的
f
f
f值之和。
怎么求这个东西呢?画一画图可以观察到,对路径上每个点,我们把它所有儿子的
f
f
f值之和累计,那么最后多算出来的贡献就是除了点
v
v
v,其余在路径上的点的
f
f
f值之和,我们把这个多算的部分减去就行了。再稍微调整一下式子,令
s
(
v
)
s(v)
s(v)为点
v
v
v所有儿子的
f
f
f值之和,减去点
v
v
v的
f
f
f值,并令当前路径为
(
x
,
y
)
(x,y)
(x,y),那么答案就可以表示为,点
v
v
v的所有儿子
f
f
f值之和,加上
v
v
v到
x
x
x路径上所有点的
s
s
s值之和(不包括点
v
v
v),再加上
v
v
v到
y
y
y路径上所有点的
s
s
s值之和(不包括点
v
v
v)。这样我们其实就是求这样一个问题:每个点有点权,询问这一点到某点
v
v
v的路径上的点权和。因为DP的过程就是自下而上的,因此每当我们处理完一个点
v
v
v,它的点权就会为它子树中所有点的贡献增加
s
(
v
)
s(v)
s(v),在DFS序上就是一个区间加、单点询问的问题,显然差分后用树状数组处理即可,那么我们就解决了这一题,时间复杂度为
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,fa[200010][20]={0},dep[200010],first[200010]={0},tot=0;
int in[200010],out[200010],tim=0,a[200010],b[200010],val[200010];
int sum[200010]={0},f[200010];
vector<int> p[200010];
struct edge
{
int v,next;
}e[200010];
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void dfs(int v)
{
in[v]=++tim;
for(int i=first[v];i;i=e[i].next)
{
dep[e[i].v]=dep[v]+1;
dfs(e[i].v);
}
out[v]=tim;
}
int lca(int a,int b)
{
if (dep[a]<dep[b]) swap(a,b);
for(int i=18;i>=0;i--)
if (dep[fa[a][i]]>=dep[b])
a=fa[a][i];
if (a==b) return a;
for(int i=18;i>=0;i--)
if (fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i))
sum[i]+=c;
}
int calc_sum(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=sum[i];
return ans;
}
void dp(int v)
{
int downsum=0;
for(int i=first[v];i;i=e[i].next)
{
dp(e[i].v);
downsum+=f[e[i].v];
}
f[v]=downsum;
for(int i=0;i<p[v].size();i++)
{
int x=a[p[v][i]],y=b[p[v][i]],V=val[p[v][i]];
f[v]=max(f[v],downsum+calc_sum(in[x])+calc_sum(in[y])+V);
}
add(in[v],downsum-f[v]);
add(out[v]+1,f[v]-downsum);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++)
{
scanf("%d",&fa[i][0]);
insert(fa[i][0],i);
}
dep[0]=-1,dep[1]=0;
dfs(1);
for(int i=1;i<=18;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i],&b[i],&val[i]);
p[lca(a[i],b[i])].push_back(i);
}
dp(1);
printf("%d",f[1]);
return 0;
}