Address
洛谷 P3724
BZOJ 4828
LOJ #2021
Solution
- 非常有意思的题
- 首先求出最多可以用多少天来打伤害
- 状态:
f[i][j] 表示要保证第
i 天的自信值至少为
j ,则前
i 天内最多可以不刷题几天
- 转移非常显然
-
f[0][j]=0,0≤j≤mc
-
f[i][j−ai]=max(f[i][j−ai],f[i−1][j]+1)
-
f[i][min(mc,j−ai+wi)]=max(f[i][min(mc,j−ai+wi)],f[i−1][j])
- 求出
ndays 表示最多可以用多少天来打伤害
-
ndays=i=1maxnj=0maxmcf[i][j]
- 然后考虑打伤害
- 利用 BFS + 哈希表预处理出一些二元组
(d,f) 构成的集合
- 这些二元组
(d,f) 的意义是可以用
d 天(包括升等级,提升讽刺能力,以及这
d 天的最后一天怼大佬,
d≤ndays )使大佬的自信值降低
f
- 可以发现这样的
(d,f) 数量是可以接受的
- 然后每个大佬分三种情况:
- (1)不怼大佬,只使用普攻。即判断是否
C≤ndays 。
- (2)怼一次大佬。即判断是否存在一个二元组
(d,f) 满足:
- ①
f≤C (自信值不能为负数)
- ②
f+ndays−d≥C (在剩下的至多
ndays−d 内使用普攻)
- (3)这也是重点:怼两次大佬。
- 将所有的二元组按照
f 为关键字从小到大排序,然后枚举第一次怼大佬的二元组
(d1,f1) ,然后这时候需要满足的第二次怼大佬情况
(d2,f2) 需要满足
-
f1+f2≤C
-
f1+f2+ndays−d1−d2≥C
- 先考虑第一个条件,显然排序之后合法的
f2 是一段前缀并且随着
f1 的增大而减小。所以按照
f 倒序枚举二元组之后 two pointers 维护
f2 合法的二元组前缀即可
- 而对于第二个条件,移项后可得
-
C−f1−ndays+d1≤f2−d2
- 设当前
f2 合法的前缀是第
1 个二元组到第
k 个二元组
- 那么我们要做的就是在前
k 个二元组中找出一个最大
f−d 作为第二次怼大佬的结果
- 也就是这时候要判断是否
-
C−f1−ndays+d1≤i=1maxk{f(i)−d(i)}
- 上面排序后第
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;
}