【题解】CF1120D Power Tree

1:重构

1.1:重构数组

将叶子节点按照 \(DFS\) 序映射成一个重构数组(长度 \(len\)

每个结点 \(x\)在重构数组上有一个覆盖区间 \([l_x,r_x]\)

目的是使数组归零,我们在 \(len+1\) 位置建立一个虚拟点,从差分的角度思考问题,目的是使 \([1,len]\) 的数值转移到 \(len+1\)

1.2:重构图

对于一次操作 \(v\) ,相当于 \(l_v\) 位置的数减去 \(x\)\(v_r+1\) 位置的数加上 \(x\) (或反过来)

相当于是一个 \(l->r+1\) 的流,或者是 \(r+1->l\) 的流,这些流最终要流向位置 \(len+1\) 的虚拟点

我们可以在 \(l\)\(r+1\) 之间建立双向边,边权为节点 \(v\) 的点权

此时新建图上他们联通,一定可以流向 \(len+1\)

2:MST

要求最小花费,此时新建图上有很多冗余边,那么原问题就是在新建图上求最小生成树

合法点求法见代码

3.代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
vector<int> mp[N];
struct Edge{
	int w,f,t,id;
	const bool operator<(const Edge b)const{
		return this->w<b.w;
	}
}e[N];
int nw[N];
int ls[N],rs[N];
int idx=0;
void dfs1(int u,int pa){
	if(mp[u].size()==1&&u!=1){
		ls[u]=rs[u]=++idx;
		return ;
	}
	ls[u]=N,rs[u]=0;
	for(auto v:mp[u]){
		if(v==pa) continue;
		dfs1(v,u);
		ls[u]=min(ls[u],ls[v]);
		rs[u]=max(rs[u],rs[v]);
	}
}
int bcj[N];
int ok[N];
int find(int x){
	if(x==bcj[x]) return x;
	return bcj[x]=find(bcj[x]);
}
signed main(){
	int n;cin>>n;
	for(int i=1;i<=n;i++) cin>>nw[i];
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		mp[a].push_back(b);
		mp[b].push_back(a);
	}
	dfs1(1,0);
	idx=0;
	for(int i=1;i<=n;i++){
		++idx;
		e[idx].w=nw[i];
		e[idx].f=ls[i];
		e[idx].t=rs[i]+1;
		e[idx].id=i;
	}
	sort(e+1,e+1+idx);
	for(int i=1;i<=idx;i++){
		//cout<<e[i].f<<" "<<e[i].t<<" "<<e[i].w<<"\n";
	}
	for(int i=1;i<=n;i++) bcj[i]=i;
	int ans=0;
	int cnt=0;
	for(int l=1;l<=idx;l++){
		int r=l;
		while(r<=idx&&e[r].w==e[l].w) r++;
		for(int i=l;i<r;i++){
			if(find(e[i].f)!=find(e[i].t)){
				if(ok[e[i].id]) continue;
				cnt++;
				ok[e[i].id]=1;
			}
		}
		for(int i=l;i<r;i++){
			int f=e[i].f,t=e[i].t;
			f=find(f);t=find(t);
			if(f==t) continue;
			bcj[f]=t;
			ans+=e[i].w;
		}	
		l=r-1;	
	}
	cout<<ans<<" ";
	cout<<cnt<<"\n";
	for(int i=1;i<=n;i++){
		if(ok[i]) cout<<i<<" ";
	}
	cout<<"\n";
	return 0;
}

4.历程

4.1 误入歧途

一开始把它拆成两个子问题,一个是求最小值,另一个是求合法点

子问题一:每个叶子 \(x\) 可以选择 \(root->x\) 路径上的最小花费(不重),此时可以堆合并求解

子问题二:根据子问题一堆中的下标初始化合法点,如果一个节点 \(x\) ,其子树中有合法点与之权值相等,则它为合法点,可以树上启发式合并求解

提交之后发现WA了,发现性质:在一个方案中,所取操作点之间,包含的叶子结点集合不能完全相同

4.2 柳暗花明

于是我把叶子结点映射成二进制位,每一个节点都对应一个二进制数,表示包括叶子结点集合

对他们高斯消元后,剩余 \(1\) 的个数刚好等于叶子结点个数,则合法

从差分角度考虑,可以将每个节点的二进制数转成两点

则判定方式转为,原矩阵覆盖所有二进制位,且没有任何两个二进制数相同

此时发现这个可以用生成树做,于是建树,求最小生成树

然后统计合法点,AC

5.反思

5.1

解决某些树问题,可以转成矩阵来做

解决某些矩阵问题,可以转成生成树来做

5.2

每个节点可以对叶子结点操作,叶子结点的某种形态是结果

那么可以将叶子结点转为数组,其余节点求出其操作区间,转成矩阵问题

并根据其性质求解

posted @ 2026-06-27 20:47  Aistyr  阅读(8)  评论(0)    收藏  举报