GZEZ "六校NOI集训" Day9
T2(sequence)
注意到只有 \(A_i \le 2\times k\) 的部分是有用的,且覆盖这些位置的线段只有 \(\le 10\) 条,启发我们使用状压。
设 \(dp[i,j,S]\) 表示考虑前 \(i\) 位,选取 \(j\) 条线段,且包含 \(i\) 位置的线段中选取情况为 \(S\) 。
跳过所有被超过 \(2\times k\) 条线段覆盖的点。
转移时首先需要处理前后两个状态的对应关系,因为存状态对线段进行了重新编号。
如图,先处理 \(S\) 对 \(U\) 的贡献,存储在 \(mx[j, S]\) 里,再用 \(U\) 的信息去更新 \(T\) 。

#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 1e5 + 7;
const int V = 1023;
typedef long long ll;
const ll inf = 1e18;
int n, m, k, c[_], cnt[2048], id[_][11], tr[2048];
ll dp[2][11][2048], mx[11][2048], ans;
std::vector <int> G[_];
std::set <int> S;
int read() {
int x = 0; char c = getchar();
while (c < '0' or c > '9') c = getchar();
while (c >= '0' and c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
int main() {
n = read(), m = read(), k = read();
lep(i, 1, n) c[i] = read(); int l, r;
lep(i, 1, m) l = read(), r = read(),
G[l].push_back(i), G[r + 1].push_back(-i);
lep(i, 0, V) {
cnt[i] = cnt[i >> 1] + (i & 1);
lep(j, 0, k) dp[0][j][i] = mx[j][i] = -inf;
}
int p = 0, lst = 0; dp[p][0][0] = 0;
lep(i, 1, n) {
for (int v : G[i]) if (v > 0) S.insert(v); else S.erase(-v);
if ((int)S.size() > 2 * k) continue;
std::memset(mx, 128, sizeof(mx)), std::memset(dp[p ^ 1], 128, sizeof(dp[p ^ 1]));
int A = 0, B = 0, t = 0;
for (int w : S) {
id[i][++t] = w;
if (!lst) continue;
lep(r, 1, 2 * k) if (id[lst][r] == w) {
A |= (1 << (r - 1)), B |= (1 << (t - 1));
tr[1 << (r - 1)] = 1 << (t - 1); break;
}
}
lep(S, 0, V) {
tr[S] = tr[S ^ (S & -S)] | tr[S & -S];
lep(j, 0, k) {
mx[j][tr[S & A]] = std::max(mx[j][tr[S & A]], dp[p][j][S]);
}
}
int up = V & ((1 << t) - 1);
lep(j, 0, k) lep(T, 0, up) if (cnt[T] <= k) { int w = j + cnt[T ^ (T & B)];
if (w > k) continue;
dp[p ^ 1][w][T] = std::max(dp[p ^ 1][w][T],
mx[j][T & B] + (cnt[T] >= ((t + 1) >> 1)) * c[i]);
ans = std::max(ans, dp[p ^ 1][w][T]);
}
lst = i, p ^= 1;
}
printf("%lld\n", ans);
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

Day9
浙公网安备 33010602011771号