CF1119F Niyaz and Small Degrees

这种要求所有点都满足一个性质的东西,我们可以用树形(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),那么对每个点都维护一颗平衡树,然后算答案就好了,为了保证复杂度,还要对边按出点的点的度数排序。

原文地址:https://www.cnblogs.com/dixiao/p/14806087.html