Solution S o l u t i o n
操作相当于是动态的做一个背包DP。
离线的话,线段树分治一下。
因为结尾是已知的,可以一边分治,得到一个修改操作,就插到线段树。
只要保证分治从左到右,每个操作都在线段树上得到了实现。
1.5×104
1.5
×
10
4
可以跑
O(n2logn)
O
(
n
2
log
n
)
了解一下。
实际上跑不到这么满????
#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> pairs;
const int N = 15151;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0; int sgn = 0;
for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
if (sgn) x = -x;
}
int n, m, q, tp;
int opt, x, y, z, d, last;
int f[20][N];
vector<pairs> node[N << 2];
inline void dp(int *f, int v, int w) {
for (int i = m - v; ~i; i--)
f[i + v] = max(f[i + v], f[i] + w);
}
inline void add(int o, int l, int r, int L, int R, int v, int w) {
if (l >= L && r <= R)
return (void)(node[o].push_back(pairs(v, w)));
int mid = (l + r) >> 1;
if (L <= mid) add(o << 1, l, mid, L, R, v, w);
if (R > mid) add(o << 1 | 1, mid + 1, r, L, R, v, w);
}
inline void divAndConq(int o, int l, int r, int dep) {
int *g = f[dep];
for (auto u: node[o])
dp(g, u.first, u.second);
if (l == r) {
read(opt);
if (opt == 1) {
read(x); read(y); read(z);
d = last * tp;
x -= d; y -= d; z -= d;
if (l != z)
add(1, 1, n, l + 1, z, x, y);
} else {
read(x);
x -= last * tp;
if (g[x] < 0) {
printf("0 0\n");
last = 0;
} else {
printf("1 %d\n", g[x]);
last = 1 ^ g[x];
}
}
return;
}
int mid = (l + r) >> 1;
for (int i = 0; i <= m; i++) f[dep + 1][i] = g[i];
divAndConq(o << 1, l, mid, dep + 1);
for (int i = 0; i <= m; i++) f[dep + 1][i] = g[i];
divAndConq(o << 1 | 1, mid + 1, r, dep + 1);
}
int main(void) {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
read(n); read(m); read(tp);
for (int i = 1; i <= m; i++)
f[0][i] = -1 << 30;
divAndConq(1, 1, n, 0);
return 0;
}