HNOI 2017 怼大佬

题意

\(m\)个大佬,第\(i\)个大佬的自信值为\(C_i\)。每次怼大佬之前,你的自信值为\(mc\),等级\(L=0\),嘲讽值 \(F=1\) 。对于每一个大佬,都有\(n\)天时间,第\(i\)天你将会被大佬打击,自信值下降\(a_i\)。如果在某一天你的自信值为负数,那么你失败了

每一天你可以进行以下操作的任意一个:

  1. 还一句嘴,使大佬自信值下降\(1\)

  2. 刷水题,使自己的自信值增加\(w_i\)

  3. 把自己的等级\(+1\)

  4. 把自己的\(F\)乘上\(L\)

  5. 怼大佬,使得大佬的自信值下降 \(F\),之后 \(L = 0,F = 1\)。这个操作可以进行 \(k=\{0,1,2\}\)

你必须在某一天使大佬的自信值恰好为 \(0\) 。如果大佬的自信值为负,则判定你失败

对于每个大佬,请判断你是否成功


解法

可以发现胜利的必要条件是自己能在大佬被怼死之前存活下来

我们设一个DP \(f[i][j]\) 为在第 \(i\) 天,剩余自信值为 \(j\) 时最多能够把多少天用来怼大佬

在该数组中取一个最大值即可求出最多能有多少天用于怼大佬,把这个天数设置为 \(D\)

可以发现怼大佬这一操作的威力只与操作 \(4,5\) 有关

一遍 BFS 求出所有的二元组 \(<D, F>\) 代表花 \(D\) 天可以对大佬造成的伤害 \(F\)

贪心的思考,对于某一个 \(F\) ,我们只需要保留最小的 \(D\) 即可,这样可以减少很多状态

你能怼死大佬有三种情况:

  1. 不怼大佬,只还嘴。满足 \(C_i \leq D\) 即可

  2. 只怼一次大佬。这时能怼死大佬需要满足存在一个二元组 \((d',f')\)使得 \(f'\leq C_i\) 并且 \(f'+D-d' \ge C_i\)。(即怼不死的部分用还嘴耗掉)

  3. 怼两次大佬。可以发现,当 \(f_1 +f_2 \leq C_i\) 并且 \(f_1+f_2+(D-d_1-d_2)\geq C_i\) 时可以将大佬怼死。我们把所有二元组按 \(f\) 排序,第一个条件用 two_pointers 维护(对于某个 \(f\) ,能与它配对的二元组是一个连续的区间),至于第二个,我们把第二个指针视作必选的二元组,那么只需要在它可以配对的区间中选一个 \(f-d\) 最大的即可


代码

#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 210;
const int MAX_S = 1e6 + 10;

#define Pii pair<int, int>
#define mp make_pair
#define x first
#define y second

int read();

int N, Q, Hp, K;
int T, mxc, top; 

int b[MAX_N], h[MAX_N], p[MAX_N];
int f[MAX_N][MAX_N * 10];

struct sta { int i, F, L; };

queue<sta> que;
Pii a[6 * MAX_S], c[MAX_S];

struct Hash_Table {
#define mod 4194303	
	
	int cap;
	int head[mod + 10];
	int nxt[mod + 10], a[mod + 10], b[mod + 10];
	
	Hash_Table() : cap(0) {}
	
	int Hash(Pii pr) {
		int x = pr.x, y = pr.y;
		return (1LL * x * 20030207 + y) & mod;
	}
	void insert(Pii pr) {
		int u = Hash(pr);
		nxt[++cap] = head[u];
		head[u] = cap;
		a[cap] = pr.x, b[cap] = pr.y;
	}
	int count(Pii pr) {
		int u = Hash(pr);
		for (int i = head[u]; i; i = nxt[i])
			if (a[i] == pr.x && b[i] == pr.y)  return 1;
		return 0;
	}
#undef mod
} Map;

inline void chkmax(int& x, int y) { x = x > y ? x : y; }

void init() {
	for (int i = 1; i <= N; ++i)
		for (int j = b[i]; j <= Hp; ++j) {
			chkmax(f[i][j - b[i]], f[i - 1][j] + 1),
			chkmax(f[i][min(j - b[i] + h[i], Hp)], f[i - 1][j]);
		}
	for (int i = 1; i <= N; ++i)
		for (int j = 0; j <= Hp; ++j)  chkmax(T, f[i][j]);
} 

void BFS() {
	que.push((sta){1, 1, 0});	
	while (!que.empty()) {
		sta fr = que.front(); que.pop();
		int I = fr.i, F = fr.F, L = fr.L;
		if (I >= T)  continue;
		que.push((sta){I + 1, F, L + 1});
		if (L > 1 && 1LL * L * F <= mxc && !Map.count(mp(1LL * L * F, I + 1))) {
			a[++top] = mp(L * F, I + 1);
			que.push((sta){I + 1, L * F, L});
			Map.insert(a[top]);
		}
	}
	int tp = 0;
	sort(a + 1, a + top + 1);
	for (int i = 1; i <= top; ++i) 
		if (a[i].x ^ a[i - 1].x)  c[++tp] = a[i];
	top = tp;
}

int main() {
	
	N = read(), Hp = read(), K = read();
	for (int i = 1; i <= N; ++i)  b[i] = read();
	for (int i = 1; i <= N; ++i)  h[i] = read();
	
	Q = read();
	for (int i = 1; i <= Q; ++i)  p[i] = read();
	for (int i = 1; i <= Q; ++i)  chkmax(mxc, p[i]);
	
	init();
	BFS(); 
	
	for (int i = 1; i <= Q; ++i) {
		if (!K) {
			puts(T >= p[i] ? "Yes" : "No"); 
			continue;
		} else if (K == 1) {
			if (T >= p[i]) { puts("Yes"); continue; }
			int fl = 0;
			for (int j = 1; j <= top; ++j) {
				if (c[j].x > p[i])  break;
				if (T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
			}
			puts(fl ? "Yes" : "No");
		} else {
			if (T >= p[i]) { puts("Yes"); continue; }
			int mn = (int)1e9, l = 1, fl = 0;
			for (int j = top; j >= 1; --j) {
				while (l < j && c[l].x + c[j].x <= p[i])  mn = min(mn, c[l].y - c[l].x), ++l;
				if (mn + c[j].y - c[j].x + p[i] <= T)  { fl = 1; break; }
				if (c[j].x <= p[i] && T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
			}
			puts(fl ? "Yes" : "No");
		}
	}
	
	return 0;
}

int read() {
	int x = 0, c = getchar();
	while (!isdigit(c))  c = getchar();
	while (isdigit(c))   x = x * 10 + c - 48, c = getchar();
	return x;	
}
posted @ 2019-10-15 20:14  四季夏目天下第一  阅读(127)  评论(2编辑  收藏  举报