CF856D Masha and Cactus
题意
给一棵树,在给定的 \(m\) 条带权边中选出若干条,使得每一个点最多包含于一个环。求选边的最大权值和。
Solution
相当于可以覆盖树上一条路径来获得特定价值。好吧真的是大炮题。
考虑定义 \(dp_i\) 表示在以 \(i\) 为根的子树中进行合法的覆盖所能得到的最大权值和。于是你考虑对于一条路径,在它的 \(lca\) 处统计它的贡献。那么对于 \(dp_u\),转移有两种情况:
- 不选取某一条以 \(u\) 为 \(lca\) 的路径,这样的话没有任何影响;
- 如果选取了一条 \((x,y)\) 的路径,那么转移应当是这条路径上所有节点不在路径上的儿子的 \(dp\) 值的和。
这样转移肯定是寄寄,所以考虑优化这个转移。考虑是否有一种方法使得能够通过加和的方式来统计这个转移。我们构造一个 \(g_u=-dp_u+\sum_{v\in son_u}dp_v\),那么对于在 \(u\) 处的转移,如果有 \((x,y)\) 路径,那么有:
解释一下,我们在转移结束之前,\(u\) 的 \(g\) 和 \(dp\) 都是 \(0\),所以相当于在路径上去掉了 \(u\),但是 \(u\) 在路径上的儿子多减了,但是 \(u\) 不在路径上的儿子没有加上,所以直接加上所有儿子的 \(dp\) 就行了。
基于此,我们可以想见 \(u\) 包括 \(u\) 的祖先,无论是 \(dp\) 还是 \(g\) 都是 \(0\)。所以我们对于边 \((x,y)\) 实际上直接加上 \(x\) 到根的 \(g\) 的和以及 \(y\) 到根的 \(g\) 的和就行了。
那这东西怎么看都可以优化,我们定义 \(h_u\) 表示 \(u\) 到根的 \(g\) 值之和。那我们转移的时候实际上就是查询这个 \(h_x+h_y+w_{(x,y)}+\sum_{v\in son_u}dp_v\)。然后每次求出一个点的 \(g\),在子树中的 \(h\) 都要加上这个 \(g\)。于是你按 \(dfn\) 展开,直接线段树维护一下就好了。(感觉这样维护有点亏,不如改成单点修改,查询到根的路径上的和,但是这样又需要树剖,麻烦)
Code
using namespace std;
const int MAXN=2e5+10;
struct Tree{int l,r,inc,sum;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
void build(int i,int l,int r){
tr[i].l=l;tr[i].r=r;tr[i].inc=tr[i].sum=0;
if(l==r) return;int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
}
void add(int i,int v){tr[i].sum+=v;tr[i].inc+=v;}
void pushdown(int i){
add(ls,tr[i].inc);
add(rs,tr[i].inc);
tr[i].inc=0;
}
void upd(int i,int l,int r,int v){
if(tr[i].l==l&&tr[i].r==r) return add(i,v);
pushdown(i);int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) upd(ls,l,r,v);else if(l>mid) upd(rs,l,r,v);
else upd(ls,l,mid,v),upd(rs,mid+1,r,v);
}
int ask(int i,int x){
if(tr[i].l==tr[i].r) return tr[i].sum;
pushdown(i);int mid=(tr[i].l+tr[i].r)>>1;
if(x<=mid) return ask(ls,x);else return ask(rs,x);
}
vector<int> e[MAXN];
int dfn[MAXN],siz[MAXN],f[MAXN][20],dep[MAXN],tot;
void predfs(int x){
rep(i,1,19) f[x][i]=f[f[x][i-1]][i-1];
siz[x]=1;dfn[x]=++tot;
for(int s:e[x]){
dep[s]=dep[x]+1;
predfs(s);
siz[x]+=siz[s];
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
int dlt=dep[x]-dep[y];
rep(i,0,19) if(dlt&(1<<i)) x=f[x][i];
if(x==y) return x;
per(i,19,0) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int dp[MAXN];
struct Trans{int x,y,w;};
vector<Trans> trs[MAXN];
void dfs(int x){
int sum=0;
for(int s:e[x]){
dfs(s);
sum+=dp[s];
dp[x]+=dp[s];
}
for(auto t:trs[x])
dp[x]=max(dp[x],ask(1,dfn[t.x])+ask(1,dfn[t.y])+t.w+sum);
upd(1,dfn[x],dfn[x]+siz[x]-1,-dp[x]+sum);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m;cin>>n>>m;
rep(i,2,n){
cin>>f[i][0];
e[f[i][0]].pb(i);
}predfs(1);
build(1,1,n);
rep(i,1,m){
int x,y,w;cin>>x>>y>>w;
trs[LCA(x,y)].pb(Trans{x,y,w});
}dfs(1);
cout<<dp[1]<<'\n';
return 0;
}