此题暴力会挺容易拿分的
暴力1:爆搜进行哪个挑战,判断一下是不是满足题目中给的要求就行。时间肯定会爆掉,计算时间复杂度为O(2^n)O(n2^n),应该可以得到8分。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int read() {
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')f = -1;
ch = getchar();
}
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
int c, T, n, m, k, d, f[N];
struct node {
int l, r, v;
} q[N];
bool cmp(node a, node b) {
if (a.r != b.r){
return a.r < b.r;
}
return a.l < b.l;
}
signed main() {
c = read(), T = read();
while (T--) {
n = read(), m = read(), k = read(), d = read();
memset(f, -0x3f, sizeof(f));
f[0] = 0;
int ans = 0;
for (int i = 1; i <= m; i++) {
int x = read(), y = read(), v = read();
q[i] = {x - y + 1, x, v};
if (y <= k){
ans += max(0ll, v - y * d);
}
}
printf("%lld\n", ans);
}
return 0;
}
暴力2(即我暴力):可以写一个dp,需要对题意进行转化,对于第i个挑战,(li,ri,vi)表示从第li=xi-yi+1天开始,到ri=xi天为止连续跑步,能够得到的能量值为vi。
设f(i,0/1)分别表示第i天强制不选/不强制不选的最大能量。在附带一个数组arr(i,j)=∑rk<j,lk=ivk来辅助。就会发现转移式子:
f(i,0)=max(f(i−1,0),f(i−1,1))
然后在实现辅助数组的时候,只需要开一维的arrj,然后每更新一次右端点i,就将满足rk=i 的挑战k 提出来。
dp代码如下(36pts):
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxn = 3e5 + 10;
int n, m, k, d;
int c,t;
int f[maxn][2], ls[maxn];
vector<pair<int, int> > q[maxn];
signed main() {
c = read();//编号
t = read();//组数
while (t--) {
n = read(), m = read(), k = read(), d = read();
int x, y, v;
for (int i = 1; i <= n; i++)q[i].clear();
memset(f, 0, sizeof(f));
memset(ls, 0, sizeof(ls));
for (int i = 1; i <= m; i++) {
x = read(), y = read(), v = read();
q[x].push_back(make_pair(x - y + 1, v));
}
int ans = 0;
for (int i = 1; i <= n; i++) {
int sum = 0;
for (auto j : q[i]) {
ls[j.first] += j.second;
}
for (int j = i; i - j < k && j > 0; j--) {
sum += ls[j];
f[i][1] = max(f[i][1], f[j - 1][0] + (j - 1) * d + sum - i * d);
}
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
ans = max(ans, max(f[i][0], f[i][1]));
}
printf("%lld\n", ans);
}
return 0;
}
然后...我们就要打开标签,偷偷瞟一眼了,动态规划+线段树。既然自己想的dp过不了,那就直接搞线段树呗,刚开始我愣是想不到这道题跟线段树有什么关系,后来看了题解才知道。在于我们每次转移都是暴力枚举左端点。而不难发现,每次枚举的左端点是一个区间,只要能够满足一个查询区间最大值的数据结构就能把复杂度降下去。但中途加入任务区间又会带来区间修改。等等,区间修改和区间最大值?这直接线段树不就行了!使用线段树来处理,每次二分最靠左的满足条件的 j(当然也可以双指针)时间复杂度O(mlogm)肯定能过的啊。
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
bool st;
const int maxn = 9e5 + 10;
struct Segment_Tree {
int n;
struct node {
ll maxx, tag;
node(ll maxx = 0, ll tag = 0): maxx(maxx), tag(tag) {}
} d[maxn << 2];
node mergenode(node l, node r) {
return node(max(l.maxx, r.maxx));
}
void pushdown(int p) {
if (d[p].tag) {
d[p << 1].tag += d[p].tag;
d[p << 1].maxx += d[p].tag;
d[p << 1 | 1].tag += d[p].tag;
d[p << 1 | 1].maxx += d[p].tag;
d[p].tag = 0;
}
}
void build(int l, int r, int p) {
if (l == r) {
d[p] = node();
return;
}
int mid = l + r >> 1;
build(l, mid, p << 1);
build(mid + 1, r, p << 1 | 1);
d[p] = mergenode(d[p << 1], d[p << 1 | 1]);
}
void update(int l, int r, int s, int t, int p, ll add) {
if (s <= l && r <= t) {
d[p].tag += add;
d[p].maxx += add;
return;
}
int mid = l + r >> 1;
pushdown(p);
if (s <= mid)update(l, mid, s, t, p << 1, add);
if (mid < t)update(mid + 1, r, s, t, p << 1 | 1, add);
d[p] = mergenode(d[p << 1], d[p << 1 | 1]);
}
node query(int l, int r, int s, int t, int p) {
if (s <= l && r <= t) {
return d[p];
}
int mid = l + r >> 1;
pushdown(p);
if (t <= mid)return query(l, mid, s, t, p << 1);
if (mid < s)return query(mid + 1, r, s, t, p << 1 | 1);
return mergenode(query(l, mid, s, t, p << 1), query(mid + 1, r, s, t, p << 1 | 1));
}
void build(int x) {
n = x;
build(0, n + 1, 1);
}
int query(int l, int r) {
return query(0, n + 1, l, r, 1).maxx;
}
void update(int l, int r, ll add) {
update(0, n + 1, l, r, 1, add);
}
} tree;
int n, m, k;
ll d, f[maxn][2];
vector<pair<int, int> > vec[maxn];
vector<pair<pair<int, int>, int> > tmp;
vector<ll> disc;
int Lk[maxn];
bool ed;
signed main() {
int testcase = read(), T = read();
while (T--) {
n = read(), m = read(), k = read(), d = read();
int x, y, v;
disc.clear();
tmp.clear();
memset(f, 0, sizeof(f));
for (int i = 1; i <= m; i++) {
x = read(), y = read(), v = read();
tmp.push_back(make_pair(make_pair(x - y + 1, x), v));
disc.push_back(x - y + 1);
disc.push_back(x);
disc.push_back(x - y);
}
disc.push_back(-1);
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
int tot = disc.size() - 1;
tree.build(tot);
for (int i = 1; i <= tot; i++)vec[i].clear();
int l = 1;
for (int i = 0; i < m; i++) {
tmp[i].first.first = lower_bound(disc.begin(), disc.end(), tmp[i].first.first ) - disc.begin();
tmp[i].first.second = lower_bound(disc.begin(), disc.end(), tmp[i].first.second) - disc.begin();
vec[tmp[i].first.second].push_back(make_pair(tmp[i].first.first, tmp[i].second));
}
for (int i = 1; i <= tot; i++) {
while (disc[i] - disc[l] >= k && l <= i)l++;
Lk[i] = l;
}
ll ans = 0;
for (int i = 1; i <= tot; i++) {
for (auto j : vec[i]) {
tree.update(1, j.first, j.second);
}
f[i][1] = tree.query(Lk[i], i) - disc[i] * d;
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
ans = max(ans, max(f[i][0], f[i][1]));
tree.update(i + 1, i + 1, f[i][0] + disc[i] * d);
}
printf("%lld\n", ans);
}
return 0;
}