[BZOJ4828][Hnoi2017]大佬(DP + 单调性)

Address

洛谷 P3724
BZOJ 4828
LOJ #2021

Solution

  • 非常有意思的题
  • 首先求出最多可以用多少天来打伤害
  • 状态: f [ i ] [ j ] f[i][j] 表示要保证第 i i 天的自信值至少为 j j ,则前 i i 天内最多可以不刷题几天
  • 转移非常显然
  • f [ 0 ] [ j ] = 0 , 0 j m c f[0][j]=0,0\le j\le mc
  • f [ i ] [ j a i ] = max ( f [ i ] [ j a i ] , f [ i 1 ] [ j ] + 1 ) f[i][j-a_i]=\max(f[i][j-a_i],f[i-1][j]+1)
  • f [ i ] [ min ( m c , j a i + w i ) ] = max ( f [ i ] [ min ( m c , j a i + w i ) ] , f [ i 1 ] [ j ] ) f[i][\min(mc,j-a_i+w_i)]=\max(f[i][\min(mc,j-a_i+w_i)],f[i-1][j])
  • 求出 n d a y s ndays 表示最多可以用多少天来打伤害
  • n d a y s = max i = 1 n max j = 0 m c f [ i ] [ j ] ndays=\max_{i=1}^n\max_{j=0}^{mc}f[i][j]
  • 然后考虑打伤害
  • 利用 BFS + 哈希表预处理出一些二元组 ( d , f ) (d,f) 构成的集合
  • 这些二元组 ( d , f ) (d,f) 的意义是可以用 d d 天(包括升等级,提升讽刺能力,以及这 d d 天的最后一天怼大佬, d n d a y s d\le ndays )使大佬的自信值降低 f f
  • 可以发现这样的 ( d , f ) (d,f) 数量是可以接受的
  • 然后每个大佬分三种情况:
  • (1)不怼大佬,只使用普攻。即判断是否 C n d a y s C\le ndays
  • (2)怼一次大佬。即判断是否存在一个二元组 ( d , f ) (d,f) 满足:
  • f C f\le C (自信值不能为负数)
  • f + n d a y s d C f+ndays-d\ge C (在剩下的至多 n d a y s d ndays-d 内使用普攻)
  • (3)这也是重点:怼两次大佬
  • 将所有的二元组按照 f f 为关键字从小到大排序,然后枚举第一次怼大佬的二元组 ( d 1 , f 1 ) (d_1,f_1) ,然后这时候需要满足的第二次怼大佬情况 ( d 2 , f 2 ) (d_2,f_2) 需要满足
  • f 1 + f 2 C f_1+f_2\le C
  • f 1 + f 2 + n d a y s d 1 d 2 C f_1+f_2+ndays-d_1-d_2\ge C
  • 先考虑第一个条件,显然排序之后合法的 f 2 f_2 是一段前缀并且随着 f 1 f_1 的增大而减小。所以按照 f f 倒序枚举二元组之后 two pointers 维护 f 2 f_2 合法的二元组前缀即可
  • 而对于第二个条件,移项后可得
  • C f 1 n d a y s + d 1 f 2 d 2 C-f_1-ndays+d_1\le f_2-d_2
  • 设当前 f 2 f_2 合法的前缀是第 1 1 个二元组到第 k k 个二元组
  • 那么我们要做的就是在前 k k 个二元组中找出一个最大 f d f-d 作为第二次怼大佬的结果
  • 也就是这时候要判断是否
  • C f 1 n d a y s + d 1 max i = 1 k { f ( i ) d ( i ) } C-f_1-ndays+d_1\le\max_{i=1}^k\{f_{(i)}-d_{(i)}\}
  • 上面排序后第 i i 个二元组为 ( d ( i ) , f ( i ) ) (d_{(i)},f_{(i)})
  • 完美解决

Code

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

const int N = 105, M = 3e6 + 5, ZZQ = 19260817;

int n, m, mc, a[N], w[N], f[N][N], ndays, len, C, maxfd[M];

struct node
{
	int l, f;
	
	friend inline bool operator < (node a, node b)
	{
		return a.l < b.l || (a.l == b.l && a.f < b.f);
	}
};

struct juruo
{
	node ct; int dis;
};

struct dalao
{
	int d, f;
} q[M];

inline bool comp(const dalao &a, const dalao &b)
{
	return a.f < b.f;
}

std::map<node, int> orz;
std::queue<juruo> que;

void bfs()
{
	int i;
	orz[(node) {0, 1}] = 1;
	que.push((juruo) {(node) {0, 1}, 0});
	q[len = 1] = (dalao) {1, 1};
	while (!que.empty())
	{
		juruo u = que.front(); que.pop();
		if (u.dis < ndays)
		{
			if (!orz[(node) {u.ct.l + 1, u.ct.f}])
			{
				orz[(node) {u.ct.l + 1, u.ct.f}] = 1;
				juruo nw;
				nw.ct = (node) {u.ct.l + 1, u.ct.f};
				nw.dis = u.dis + 1;
				que.push(nw);
			}
			if (u.ct.l && 1ll * u.ct.l * u.ct.f <= 1000000000 &&
				!orz[(node) {u.ct.l, u.ct.l * u.ct.f}])
			{
				orz[(node) {u.ct.l, u.ct.l * u.ct.f}] = 1;
				juruo nw;
				nw.ct = (node) {u.ct.l, u.ct.l * u.ct.f};
				nw.dis = u.dis + 1;
				q[++len] = (dalao) {nw.dis + 1, nw.ct.f};
				que.push(nw);
			}
		}
	}
	std::sort(q + 1, q + len + 1, comp);
	maxfd[1] = q[1].f - q[1].d;
	For (i, 2, len) maxfd[i] = Max(maxfd[i - 1], q[i].f - q[i].d);
}

int main()
{
	int i, j, p;
	n = read(); m = read(); mc = read();
	For (i, 1, n) a[i] = read();
	For (i, 1, n) w[i] = read();
	For (i, 1, n)
	{
		For (j, a[i], mc)
		{
			f[i][j - a[i]] = Max(f[i][j - a[i]], f[i - 1][j] + 1);
			f[i][Min(mc, j - a[i] + w[i])]
				= Max(f[i][Min(mc, j - a[i] + w[i])], f[i - 1][j]);
		}
		For (j, 0, mc) ndays = Max(ndays, f[i][j]);
	}
	bfs();
	while (m--)
	{
		C = read();
		bool res = 0;
		if (!ndays)
		{
			puts("0");
			continue;
		}
		if (C <= ndays)
		{
			puts("1");
			continue;
		}
		For (i, 1, len)
			if (q[i].f <= C && C - q[i].f <= ndays - q[i].d)
				{res = 1; break;}
		if (res)
		{
			puts("1");
			continue;
		}
		p = 1;
		Rof (i, len, 1)
		{
			while (p <= len && q[i].f + q[p].f <= C) p++;
			if (p > 1 && C - ndays + q[i].d - q[i].f <= maxfd[p - 1])
			{
				res = 1;
				break;
			}
		}
		puts(res ? "1" : "0");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/83959794