【HDU5932 2016CCPC东北地区大学生程序设计竞赛 - 重现赛 K】【树上背包 贪心乱搞】Backpack on Tree 物品成本只有12345下的树上背包

84 篇文章 5 订阅
48 篇文章 0 订阅

Backpack on Tree

Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 43    Accepted Submission(s): 14


Problem Description
There is a rooted tree with n nodes. For each node i, there is an item whose volume is   ci  and value is   vi  and if node i is not the root, it is guaranteed that   |subtreei|23|subtreefatheri| .Bacon wants to pick items in   subtrees  so that their total volume is exactly t. Help Bacon determine the maximal total value of items he can pick.
 

Input
The first line contains one integer T( 1T40 ) and there are exactly T test cases below.

For each test case, the first line contains one integer n ( 1n2×104 ).

The following n - 1 lines describe edges in the tree. Each line contains two integers   ai  and   bi(1ai,bin,aibi)  describing an edge of the tree.

For the following n lines, the i-th line contains two integers   ci  and   vi(1ci5,1vi109) .

Next line contains one integer the number of queries Q and each of the following Q lines contains two integers   si  and   ti(1sin,1ti105)  as a query.

Note that node 1 is the root of the tree.

There is no more than 4 test cases that n is greater than   104 , and no more than 10  test cases that n is greater than   103 . sum of all Q are not greater than   2×105 .
 

Output
For each test case, first line contains "Case #x:", where x indicates the number of test cases (starting from 1).

Then print Q lines and the i-th line contains the answer of the i-th query. Print -1 for the query if there is no way to pick items in   subtrees  with total volume t.
 

Sample Input
  
  
2 5 1 2 1 3 1 4 1 5 1 1 2 2 3 3 4 4 5 5 3 1 15 2 2 3 3 5 1 2 1 3 1 4 4 5 5 123 3 4543 4 21 1 1231 2 12 3 1 5 5 2 4 4
 

Sample Output
  
  
Case #i: 15 2 3 Case #2: 4555 12 -1
Hint
The tree in first case looks like the picture above, For query subtree_s =1,t= 15,we should pick items in subtree 1. only method is to pick all items in subtree 1 and get value 15.
 

Source


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2e4 + 10, Z = 1e9 + 7;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
struct Edge
{
	int w, nxt;
}edge[N << 1]; int first[N], id;
struct A
{
	int c, v;
	bool operator < (const A&b)const
	{
		return (LL)v * b.c > (LL)b.v * c;
	}
}a[N];
vector<A>b[N];
vector< pair<int,int> >q[N];
int sumc[N];
LL sumv[N];
const int M = 60;
const LL MX = 0x7f7f7f7f7f7f7f7f;
const LL MN = 0xbfbfbfbfbfbfbfbf;
LL mn[N][M];	//mn[i]表示空间容量为i条件下最大收益
LL mx[N][M];	//mx[i]表示空间容量为i条件下最小收益
LL ans[(int)1e5 + 10];
int SZ;
int find(int m)
{
	int l = 0;
	int r = SZ - 1;
	while (l < r)
	{
		int mid = (l + r + 1) >> 1;
		if (sumc[mid] <= m)l = mid;
		else r = mid - 1;
	}
	return l;
}
void ins(int x, int y)
{
	edge[++id] = { y,first[x] };
	first[x] = id;
}
void dfs(int x, int fa)
{
	b[x].clear(); b[x].push_back(a[x]);
	for (int z = first[x]; z; z = edge[z].nxt)
	{
		int y = edge[z].w;
		if (y == fa)continue;
		dfs(y, x);
		b[x].insert(b[x].end(),b[y].begin(), b[y].end());
		b[y].clear();
	}
	sort(b[x].begin(), b[x].end());
	int g = SZ = b[x].size();
	for (int i = 0; i < g; ++i)
	{
		sumc[i] = b[x][i].c; if (i)sumc[i] += sumc[i - 1];
		sumv[i] = b[x][i].v; if (i)sumv[i] += sumv[i - 1];
	}
	for (int i = 0; i < g; ++i)
	{
		for (int j = 0; j < M; ++j)
		{
			mn[i][j] = MX;
			mx[i][j] = MN;
		}
	}

	//性价比最小的更新要从前缀取得
	if (!q[x].size())return;
	for (int i = 0; i < g; ++i)
	{
		mn[i][0] = 0;
		mn[i][b[x][i].c] = b[x][i].v;
		if (i > 0)for (int j = 0; j < M; ++j)
		{
			gmin(mn[i][j], mn[i - 1][j]);
			if (j >= b[x][i].c) gmin(mn[i][j], mn[i - 1][j - b[x][i].c] + b[x][i].v);
		}
	}
	//性价比最大的更新要从后缀取得
	for (int i = g - 1; i >= 0; --i)
	{
		mx[i][0] = 0;
		mx[i][b[x][i].c] = b[x][i].v;
		if(i < g - 1)for (int j = 0; j < M; ++j)
		{
			gmax(mx[i][j], mx[i + 1][j]);
			if (j >= b[x][i].c) gmax(mx[i][j], mx[i + 1][j - b[x][i].c] + b[x][i].v);
		}
	}
	for (auto it : q[x])
	{
		int o = it.first;
		int m = it.second;
		ans[o] = -1;
		if (m > sumc[g - 1]) continue;
		if (m < sumc[0]) { gmax(ans[o], mx[0][m]); continue; }
		int p = find(m);
		if (m == sumc[p]) { ans[o] = sumv[p]; continue; }
		//sumc[p] <= m <= sumc[p + 1]
		int add = m - sumc[p];
		for (int i = 0; i + add < M; ++i)if (mn[p][i] <= 1e9 && mx[p + 1][i + add] >= 0)
			gmax(ans[o], sumv[p] - mn[p][i] + mx[p + 1][i + add]);
	}
}
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
		{
			first[i] = 0;
			q[i].clear();
		}id = 1;
		for (int i = 1; i < n; ++i)
		{
			int x, y; scanf("%d%d", &x, &y);
			ins(x, y); ins(y, x);
		}
		for (int i = 1; i <= n; ++i)
		{
			scanf("%d%d", &a[i].c, &a[i].v);
		}
		scanf("%d", &m);
		for (int i = 1; i <= m; ++i)
		{
			int x, y; scanf("%d%d", &x, &y);
			q[x].push_back({ i, y });
		}
		dfs(1, 0);
		printf("Case #%d:\n", casei);
		for (int i = 1; i <= m; ++i)printf("%lld\n", ans[i]);
	}
	return 0;
}
/*
【trick&&吐槽】
1,mx[x][0]和mx[x][0]要设置为0
2,我们更新答案的时候,一定要详细考虑边界答案的影响
3,v的值不会爆int,但是v*c的值却会爆int,一定要小心!

【题意】
给你一棵有n(2e4)个节点的树
对于节点i拥有成本c[i](1~5)和价值v[i](1~1e9)
如果节点i不是根,则该节点子树的大小一定比父节点子树大小的2/3要小
(也就是说树的深度不会很深)
有q(2e5)个询问
对于每个询问,给定子树根节点X和总成本大小C(1e5)
我们要在子树X中找到总成本之和恰好为C的价值之和最大的物品,输出这个价值

【类型】
贪心 背包 乱搞

【分析】
本题的特殊点之处有
1,深度不深,大概只有26~27层about
2,物品成本很小,只有[1~5],但是背包容量却很大,可达1e5

思考——
1,我们可以把询问离线化。
	然后dfs,对于每棵树,便可以该棵树在某个容量下所对应的最优匹配答案
2,背包
	如果做暴力的背包,复杂度是物品数*背包容量,复杂度是吃不消的
	不过因为物品的体积只有{1,2,3,4,5}
	所以如果背包的容量很大
	我们可以考虑一开始贪心性价比最高的
	然后对于最后的差值,我们去把这个差值弥补掉。
	
	如何维护这个差值呢?
	我们维护在每个点之前的空间容量为j的条件下最小的收益,我们可以考虑在其基础上剔除
	我们维护在每个点之后的空间容量为j的条件下最高的收益,我们可以考虑再起基础上增加

	这样我们得到sumc[p] <= m <= sumc[p + 1],定义add=m-sumc[p]
	我们可以考虑去在[0~p]中-(w),在[p+1~g)中再+(w+add)
	实现在这个条件下,就使得我们可以达到最优值。
	
	M的上限设置为60比较保险,但是设置小了也可以AC =w=

【时间复杂度&&优化】
树的深度不超过30
节点数为n,每次DP的复杂度为n * M
总复杂度为O(30 * n * M)
可以控制在百万级别

【数据】
100
5
1 2
1 3
1 4
4 5
5 123
3 4543
4 21
1 1231
2 12
1
1 5

*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值