pta 完美树

很明显考察的是树形dp,我们设dp[v][0/1]为结点v子树0/1比1/0多1个的最小代价,对于一个父节点u,它的最小代价又它的若干个子节点的最小代价转移而来,我们可以发现,子树结点为偶数的子结点,不对结点u的状态产生影响,因此,我们可以把这些结点数为偶数的代价累加道答案中。对于剩下的子节点,设子节点数为k,问题转化为取出k/2+1个dp[v][0]和k/2个dp[v][1],使其代价和最小,转移到dp[u][0],同理,dp[u][1]转化为取出k/2+1个dp[v][1]和k/2个dp[v][0],使其代价和最小。

ac代码:

#include<bits/stdc++.h>
#define ll long long
#define ios ios::sync_with_stdio(0)
#define pii pair<int,int>
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dwn(i,a,b) for(int i=a;i>=b;i--)
#define repp(i,a,b) for(int i=a;i<b;i++)
#define dwnn(i,a,b) for(int i=a;i>b;i--)
#define lowbit(x) x&-x
#define mm memset
#define endl '\n'
#define INF 0x3f3f3f3f
#define debug(x) cout<<"x = "<<x<<endl
#define PB push_back
using namespace std;
const int N = 1e5 + 5;
ll n,c[N],p[N],k,dp[N][2],fa[N],root,ans;
bool vst[N];
vector<int> g[N];
void dfs(int u,int fa) {
	for(auto v : g[u]) {
		if(v == fa) continue;
		dfs(v,u);
	}
	vector<int> ji;
	ll res = 0;
	if(c[u] != 0) res += p[u],ji.PB(-p[u]);
	else ji.PB(p[u]);
	for(auto v : g[u]) {
		if(vst[v]) continue;
		res += dp[v][0];
		ji.PB(dp[v][1] - dp[v][0]);
	}
	int num = ji.size();
	sort(ji.begin(),ji.end());
	ll sum = 0;
	for(int i = 0; i < num / 2; i ++) 
		sum += ji[i];
	if(num & 1) {
		dp[u][0] = res + sum;
		dp[u][1] = dp[u][0] + ji[num / 2];
	}
	else {
		vst[u] = true;
		ans += res + sum;
	}
}
void solve() {
	cin>>n;
	rep(i,1,n) {
		cin>>c[i]>>p[i]>>k;
		rep(j,1,k) {
			int v;
			cin>>v;
			g[i].PB(v);
			fa[v] = i;
		}
	}
	rep(i,1,n) {
		if(fa[i] == 0) {
			root = i;
			break;
		}
	}
	dfs(root,fa[root]);
	if(vst[root]) ans += min(dp[root][0],dp[root][1]);
	cout<<ans<<endl;
}
int main() {
	ios;
	solve();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值