[cqbzoj#10644]鱼肉炸弹题解

题目

题目描述

舒克和贝塔终于下定决心要去营救被关押在众猫聚居的A城中的大米。
A城的构造是很奇怪的。A城中的所有N栋建筑沿着一条直线排列,没有两栋楼的高度相同,而大米就被关押在其中的某栋建筑中。每一栋建筑的顶上都有一些猫在看守。如果按照从一端到另一端的顺序将所有的建筑编号为1~N,那么第i栋建筑的高度为Hi,顶上开始时的猫的数量为Ci。
每一只猫不但可以看守住其所在建筑的楼顶,还可以看守住那些比它所在建筑要低的楼的楼顶,前提是没有被其他楼挡住。A城中的建筑是很高的,高到可以忽略他们之间的距离和它们的水平面积。于是可以认为,楼i上的猫能够看守楼j的楼顶,当且仅当楼i的高度不低于楼j,且楼i到楼j之间的所有楼房的高度都低于楼i。
现在神勇的贝塔已经潜入A城内部营救大米,而舒克则负责驾驶直升机提供空中支援。按照约定,贝塔找到并救出大米后会爬上楼顶释放信号让舒克前来接应。
舒克的飞机上装备了K枚鱼肉炸弹。每一枚鱼肉炸弹都可以在被投放到某一座楼的楼顶一段时间后使该楼所有猫失去行动能力。假设第i栋楼被Si只猫看守(注意Si只猫包括在该楼上的Ci只,已经在其他楼上的所有能看守该楼顶的猫),他希望使用这k枚鱼肉炸弹能够使的Si的最大值最小。请输出这个最小值。

输入格式

输入格式:

第一行有两个整数N和K。

第二行至第n+1行有两个整数,依次是编号为1的楼到编号为N的楼的高度(Hi)和楼顶的猫数(Ci).

输出格式

你只需要输出一个整数,表示使用K枚炸弹所能达到的Si中的最大值最小能是多少。

样例

样例输入

3 2
1 2
3 1
2 2

样例输出

1

数据范围与提示

数据规模:(1<=N<=100000,1<=k<=5,1<=Hi<=10^9,0<=Ci<=10^9).

思路

笔者之前的错误理解

其实当我们看到高度高的猫可以看到高度低的猫所看管的楼时,我们就可以知道,这是一道树形DP
对于楼层高的猫,当它们遇到了鱼肉炸弹,就会对它自身以及它所能看管的楼的(s[i])产生影响

于是笔者就很自然的想到了一个节点(一栋楼)的父亲就是它能看管的点
但是笔者很快就hack掉了自己。
因为当我们遇到这种情况时,就不符合一棵树了,(一个点有多个父节点
批注 2020-12-18 135617.png
批注 2020-12-18 135705.png

正确思路

那么我们以高度高的节点为父亲呢?但如果按照我们之前的(dp)方式(二叉苹果树,有线电视网),子节点对与其父节点产生影响,这是不符合题意的,但如果父节点对子节点产生影响呢?就可以把这个问题迎刃而解了。

当然这么妙的想法肯定不是笔者想到的对吧?笔者虽然自己思考了2hours但是就是没有想到这么妙的思路。
参考链接orz

代码

//fuck you bitch
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100005;

struct node{
	int lc, rc;
	bool f;
};
int n, k; 
long long h[maxn];
long long c[maxn];  //1e9 开long long
node son[maxn];

long long st[maxn][50];
int side[maxn][50];
long long dp[maxn][50];
int highest = 0, hw;

void pret(){
	for(int i = 1; i < maxn; i ++){
		for(int j = 0; j < 25; j ++){
			dp[i][j] = 0x3f3f3f3f;
		}
	}
}

void rmq(int n){
	for(int i = 1; i <= n; i ++){
		st[i][0] = h[i];
		side[i][0] = i;
	}
	for(int j = 1; (1 << j) <= n; j ++){
		for(int i = 1; i + (1 << j) - 1 <= n; i ++){
			st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
//			printf("%d %d %d", st[i][j], st[i][j - 1], st[i + (i << (j - 1))]);
			if(st[i][j] == st[i][j - 1]){
				side[i][j] = side[i][j - 1];
//				printf("%d %d %lld %d
", i, j, st[i][j], side[i][j]);
			}
			else{
				side[i][j] = side[i + (1 << (j - 1))][j - 1];
//				printf("%d %d %lld %d
", i, j, st[i][j], side[i][j]);
			}
		}
	}
//	printf("
");
//	for(int i = 1; i <= n; i ++){
//		for(int j = 0; i + (1 << j) <= n; j ++){
//			printf("%d ", side[i][j]);
//		}
//		printf("
");
//	}
}

int query(int l, int r){
	int k = log2(r - l + 1);
	
	if(st[l][k] > st[r - (1 << k) + 1][k]){
		return side[l][k];
	}
	return side[r - (1 << k) + 1][k];
}
int build_tree(int l , int r, int root){
	if(l > r)return 0;
	if(l == r){
		return l;
	}
	int maxs;
	maxs = query(l, r);
	son[maxs].lc = build_tree(l, maxs - 1, maxs);
	son[maxs].rc = build_tree(maxs + 1, r, maxs);
	return maxs;
}

void dfs(int x){
	if(x == 0)return;
	if(son[x].lc)dfs(son[x].lc);
	if(son[x].rc)dfs(son[x].rc);
	for(int i = 0; i <= k; i ++){
		for(int j = 0; j <= k; j ++){
			dp[x][i + j] = min(max(dp[son[x].lc][i], dp[son[x].rc][j]) + c[x], dp[x][i + j]);
			dp[x][i + j + 1] = min(max(dp[son[x].lc][i], dp[son[x].rc][j]), dp[x][i + j + 1]);
		}
	}
}


int main() {
	scanf("%d %d", &n, &k);
	pret();
	for(int i = 1; i <= n; i ++){
		scanf("%lld %lld", &h[i], &c[i]);
	}
	rmq(n);
	hw = build_tree(1, n, 0);
	dfs(hw);
	printf("%lld", dp[hw][k]);
	return 0;
} 
夜空中最亮的星,请照亮我前行
原文地址:https://www.cnblogs.com/Nefelibata/p/14154613.html