4593: [Shoi2015]聚变反应炉

4593: [Shoi2015]聚变反应炉

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 34   Solved: 19
[ Submit][ Status][ Discuss]

Description

曾经发明了零件组装机的发明家SHTSC又公开了他的新发明:聚变反应炉--一种可以产生大量清洁能量的神秘装置
。众所周知,利用核聚变产生的能量有两个难点:一是控制核聚变反应的反应强度,二是使用较少的能量激发聚变
反应。而SHTSC已经完美解决了第一个问题。一个聚变反应炉由若干个相连的聚变块组成,为了能够使得聚变反应
可控,SHTSC保证任意两个聚能块都可以通过相互之间的链接到达,并且没有一个聚能块可以不重复经过一个链接
回到它自己。但是第二个问题SHTSC还没有完全解决。在他设计的聚变反应炉当中,每个聚变块都需要一定的初始
能量di来进行激发,不过SHTSC不需要手动激发所有聚变块,这是因为一旦一个聚变块被激发,则会向与其直接相
连的所有还未被激发的聚变块传送ci个单位的能量。这样后被触发的聚变块可以以更低的初始能量来激发,甚至可
能不需要额外的外界能量就可自行激发,从而降低了总激发能量的消耗。现在给出了一个聚变反应炉,求至少要多
少能量才能激发所有聚变块。

Input

第一行一个整数n。表示共有n个聚能块,由1-n编号。
第二行n个整数,表示di。
第三行n个整数,表示ci。
以下n-1行每行两个整数,u,v。表示编号为u和v的聚能块是相连的。
输入保证符合题目描述。
Type A :ci <= 1,有 n<=100000。
Type B :ci <= 5,有n<=2000。
对于所有的数据,1<=di, Sum(di)<=10^9。输入保证符合题目描述。

Output

一行一个整数,表示至少需要多少个单位的能量才能激发所有聚变块。

Sample Input

5
1 1 1 1 1
1 1 1 1 1
1 2
2 3
3 4
4 5

Sample Output

1
//样例1:只需要触发任意一个聚变块即可激活整个聚变反应装置。

HINT

Source

By 佚名上传

[ Submit][ Status][ Discuss]



一道我想不到的树dp。。。

题干给出的ci是一个传递只与边有关的东西。。

也就是说,显然,一个点被激发的先后,只影响其周围的点,然后再考虑传递之类

先将无根树转换成有根数,那么每条边就看成连接y与其父亲x,状态就在这上面设

令fi:先激发i的父亲再激发i,这时激发整个i的子树需要的最少花费

令gi:先激发i再激发i的父亲,这时激发整个i的子树需要的最少花费

对于任意i,不妨设最优方案里i的子树中,

t∈{选择先激发i}  k∈{选择先激发自己}

那么fi = ∑ft + ∑gk + di - cfa - ∑ck

       gi = ∑ft + ∑gk + di - ∑ck

从递推式可以看到,对于任意的i,总有fi <= gi

并且递推式中的di - cfa - ∑ck 以及 di - ∑ck 一定是非负的

对于typA:

对于任意点i,显然fi与gi的差值只能是0或1,考虑i的父亲x如何在fi与gi中选择

那么如果fi == gi,选gi一定更优,因为这样对于答案至少作出了c[i]的贡献(在允许的情况下贡献给父亲)

如果gi - fi == 1,取fi一定更优,因为如果选择gi,gi中多出的1能够被抵消,一定得是父亲有得扣才行(如果儿子们贡献太多,父亲的激发根本不需花费),反之如果选择fi,那么父亲稳定给儿子1点能量,也就能对答案贡献-1了

基于以上两个贪心搞出所有的fi,gi即可

对于typB:

此时fi与gi的差值就属于[0,5]了。。

如果过多地选择fi,那么激活x可能就开销很大

如果过多地选择gi,激活各个儿子开销大不说,x还不一定能享受到它们的贡献(因为x需要的能量有上界)

这样就有些类似一个背包

定义Fi,j:对于前i个子孙,给x贡献了j点能量,需要的最小开销

和01背包一样地递推

利用F数组求出所有的fi,gi


然而第一发代码F数组边界判错了。。GG


综上。。。此题不单单是树dp。。。还考察贪心。。GG。。。

总结自网上某神犇的题解

#include
#include
#include
#include
#include
#include
using namespace std;

const int maxn = 1E5 + 10;
typedef long long LL;
const LL INF = ~0U>>1;

int n,d[maxn],c[maxn];
LL f[maxn],g[maxn],F[maxn];
bool flag;

vector  v[maxn];

void Dfs(int x,int fa)
{
	int siz,tc;
	siz = tc = 0;
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i];
		if (to == fa) continue;
		++siz;
		tc += c[to];
		Dfs(to,x);
	}
	if (flag) {
		for (int i = 1; i <= tc; i++) F[i] = INF;
		F[0] = 0; 
		for (int i = 0; i < v[x].size(); i++) {
			int to = v[x][i];
			if (to == fa) continue;
			for (int j = tc; j >= 0; j--) {
				 LL A = F[j] + f[to];
				 LL B = j >= c[to]?F[j-c[to]]+g[to]:INF;
				 F[j] = min(A,B);
			}
		}
		LL Min = INF;
		for (LL i = 0; i <= tc; i++)
			Min = min(Min,F[i] - i);
		g[x] = 1LL*d[x] + Min;
		Min = INF;
		for (LL i = 0; i <= tc; i++)
			if (i + c[fa] <= d[x])
				Min = min(Min,F[i] - i);
			else break;
		f[x] = 1LL*(d[x] - c[fa]) + Min;
	}
	else {
		LL t1,t2;
		t1 = t2 = 0;
		for (int i = 0; i < v[x].size(); i++) {
			int to = v[x][i];
			if (to == fa) continue;
			if (!c[to]) t1 += f[to];
			else {
				if (g[to] == f[to]) t1 += g[to],t2 += c[to];
				else t1 += f[to];
			}
		}
		g[x] = t1 + max(1LL*d[x] - t2,0LL);
		f[x] = t1 + max(1LL*d[x] - t2 - 1LL*c[fa],0LL);
	}
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	n = getint();
	for (int i = 1; i <= n; i++) d[i] = getint();
	for (int i = 1; i <= n; i++) {
		c[i] = getint();
		if (c[i] > 1) flag = 1;
	}
	for (int i = 1; i < n; i++) {
		int x = getint();
		int y = getint();
		v[x].push_back(y);
		v[y].push_back(x);
	}
	int Root = n/2;
	Dfs(Root,0);
	cout << g[Root];
	return 0;
}

你可能感兴趣的:(dp,贪心)