Loading

CF856D Masha and Cactus

题意

给一棵树,在给定的 \(m\) 条带权边中选出若干条,使得每一个点最多包含于一个环。求选边的最大权值和。

Solution

相当于可以覆盖树上一条路径来获得特定价值。好吧真的是大炮题。

考虑定义 \(dp_i\) 表示在以 \(i\) 为根的子树中进行合法的覆盖所能得到的最大权值和。于是你考虑对于一条路径,在它的 \(lca\) 处统计它的贡献。那么对于 \(dp_u\),转移有两种情况:

  1. 不选取某一条以 \(u\)\(lca\) 的路径,这样的话没有任何影响;
  2. 如果选取了一条 \((x,y)\) 的路径,那么转移应当是这条路径上所有节点不在路径上的儿子的 \(dp\) 值的和。

这样转移肯定是寄寄,所以考虑优化这个转移。考虑是否有一种方法使得能够通过加和的方式来统计这个转移。我们构造一个 \(g_u=-dp_u+\sum_{v\in son_u}dp_v\),那么对于在 \(u\) 处的转移,如果有 \((x,y)\) 路径,那么有:

\[dp_u=\sum_{v\in (x,y)}g_v+\sum_{v\in son_u}dp_v \]

解释一下,我们在转移结束之前,\(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;
}
posted @ 2022-07-19 22:01  ZCETHAN  阅读(29)  评论(0编辑  收藏  举报