【题解】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
每个节点可以对叶子结点操作,叶子结点的某种形态是结果
那么可以将叶子结点转为数组,其余节点求出其操作区间,转成矩阵问题
并根据其性质求解

浙公网安备 33010602011771号