这种要求所有点都满足一个性质的东西,我们可以用树形(dp)做。
先考虑一个暴力的(dp)怎么做呢。
设(f[u][1/0])为(u)点满足性质的,和父节点的边删不删的最小值。
那么有(f[u][0] = sum{min(f[v][1] + w,f[v][0])})
同理也有(f[u][1])
那么考虑先设(f[u][0] = sum f[v][0])
往一个堆里丢入(f[v][1] + w - f[v][0])取最前面的负数点,以及一些为了满足答案的正数点的和加入(f[u][0])即可
(f[u][1])则同理。
CF1119F Niyaz and Small Degrees
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define N 250005
struct P{int to,next,v;}e[N << 1];
ll n;
int cnt,head[N];
inline void add(int x,int y,int v){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = v;
head[x] = cnt;
}
ll X;
ll f[N][2];
inline void dfs(int u,int fa){
std::vector<long long>val;
f[u][0] = f[u][1] = 0;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa)
continue;
dfs(v,u);
val.push_back(f[v][1] + e[i].v - f[v][0]);
f[u][0] += f[v][0];
}
f[u][1] = f[u][0];
std::sort(val.begin(),val.end());
int s = val.size();
for(int i = 0;i < s && (i <= s - X - 1|| val[i] < 0);++i)
f[u][1] += val[i];
if(!X){f[u][0] = 0x3f3f3f3f3f;return;}
++s;
for(int i = 0;i < s - 1 && (i <= s - X - 1 || val[i] < 0);++i)
f[u][0] += val[i];
}
int main(){
scanf("%lld",&n);
for(int i = 1;i <= n - 1;++i){
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
for(X = 0;X < n;++X)
dfs(1,0),std::cout<<f[1][1]<<" ";
}
复杂度(O(n^2log))
考虑怎么优化这个复杂度,我们发现有些点如果度数小于当前有的度数,那么他的(f[u][1],f[u][0])都是(0),那么相当于只往和他相邻的点的堆里加入(w),那么对每个点都维护一颗平衡树,然后算答案就好了,为了保证复杂度,还要对边按出点的点的度数排序。