JOISC 2019 题解
Day1 T1
三维偏序板子。
Day1 T2
一种树分治。
随便找一个点当作根 dfs,随机一个点 \(x\),然后通过 query,能找出在链 \(rt \to x\) 上的点有哪些。以及每个点属于毛毛虫上哪个节点的子树。然后把这条链拆了,分治下去。由于度数限制,询问次数 37000 左右。(不懂怎么证的。
点击查看代码
#include <bits/stdc++.h>
#include <assert.h>
#include "meetings.h"
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <utility>
#include <vector>
using namespace std;
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
#define wj(name) freopen(#name".in", "r", stdin); \
freopen(#name".out", "w", stdout);
inline int read() {
int x = 0, v = 1, ch = getchar();
while ('0' > ch || ch > '9') {
if (ch == '-')
v = 0;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
x = (x * 10) + (ch^'0');
ch = getchar();
}
return v ? x : -x;
}
const int MAX = 2005;
vector<int> G[MAX], son[MAX];
int fa[MAX];
int rt, n;
int cmp(int x, int y) {
return Query(rt, x, y) == x;
}
mt19937 rad(233);
void dfs(vector<int> S, int x) {
vector<int> chain;
vector<int>().swap(son[x]);
// vector<vector<int> > son(n);
int y = rad() % S.size();
y = S[y];
for (int z : S)
if (z != y) {
int w = Query(x, y, z);
if (w == z)
chain.ep(z);
else
son[w].ep(z);
}
rt = x;
sort(chain.begin(), chain.end(), cmp);
chain.ep(y);
for (int z : chain)
Bridge(min(rt, z), max(rt, z)), rt = z;
if (son[x].size())
dfs(son[x], x);
for (int z : chain)
if (son[z].size())
dfs(son[z], z);
}
void Solve(int N) {
::n = N;
vector<int> S;
for (int i = 1; i < N; ++ i)
S.ep(i);
dfs(S, 0);
}
Day 1 T3
咕咕咕
Day 2 T1
绝对值的去除是套路。正反做两遍。 考虑 \(h_j - h_i, i < j\) 的最大值。
扫描线。考虑一种套路,枚举右端点 \(j\),线段树上维护左端点答案。
对于每个 \(i\),\(i + a_i \le j \le i + b_i\)。那么开个 vector,当 \(j = i + a_i\),线段树上修改 \(i\) 位置为 \(h_i\),然后到 \(j = i + b_i + 1\),把 \(i\) 修改为 \(- h_i\)。然后区间加 \(h_j\),再区间减 \(h_j\)。
维护历史最大值。
坑:
- 先减后加。指 \(- h_i\) 先作用。
- 单点修改时,不能写成 \(max_his = maxa = v\)。(估计就我这么瞎搞( )
点击查看代码
// 僕らタイムフライヤー \
時を駆け上がるクライマー \
時のかくれんぼ \
はぐれっこはもういやなんだ
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
#define int long long
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
#define wj(name) freopen(#name".in", "r", stdin); \
freopen(#name".out", "w", stdout);
inline int read() {
int x=0, v=1,ch=getchar();
while('0'>ch||ch>'9') {
if(ch=='-') v=0;
ch=getchar();
}while('0'<=ch&&ch<='9') {
x=(x*10)+(ch^'0');
ch=getchar();
}return v?x:-x;
}
const int MAX = 2e5 + 5, INF = 2e9;
int n, T;
int h[MAX], a[MAX], b[MAX];
vector<int> G[MAX], U[MAX];
struct Q{
int l, r;
Q() {}
void init() { l = read(), r = read(); }
} q[MAX];
// 查询区间历史最大值。
struct node {
// 区间加,区间历史最大值。
int maxa, maxb;
int add, hadd;
void upd(int k1, int k2) {
maxb = max(maxb, maxa + k2);
maxa += k1;
hadd = max(hadd, add + k2);
add += k1;
}
} tr[MAX << 2];
#define rt 1, 1, n
#define ls x << 1, l, mid
#define rs x << 1 | 1, mid + 1, r
void ps(int x) {
tr[x].maxa = max(tr[x<<1].maxa, tr[x<<1|1].maxa);
tr[x].maxb = max(tr[x<<1].maxb, tr[x<<1|1].maxb);
}
void pd(int x) {
tr[x<<1].upd(tr[x].add, tr[x].hadd);
tr[x<<1|1].upd(tr[x].add, tr[x].hadd);
tr[x].add = tr[x].hadd = 0;
}
void add(int x, int l, int r, int s, int t, int v) {
if(s <= l && r <= t) { tr[x].upd(v,v); return ; }
int mid = l + r >> 1; pd(x);
if(s <= mid) add(ls, s, t, v);
if(mid < t) add(rs, s, t, v);
ps(x);
}
void mdf(int x, int l, int r, int s, int v) {
if(l == r) {
tr[x].maxa = v;
tr[x].maxb = max(tr[x].maxb, v);
return ;
} pd(x);
int mid = l + r >> 1;
if(s <= mid) mdf(ls, s, v);
else mdf(rs, s, v);
ps(x);
}
int hans = -INF, ans[MAX];
void qry(int x, int l, int r, int s, int t) {
if(s <= l && r <= t) {
hans = max(hans, tr[x].maxb);
return ;
} int mid = l + r >> 1; pd(x);
if(s <= mid) qry(ls, s, t);
if(mid < t) qry(rs, s, t);
ps(x);
}
void build(int x, int l, int r) {
tr[x].add = tr[x].hadd = 0;
if(l == r) {
tr[x].maxa = tr[x].maxb = -INF;
return ;
} int mid = l + r >> 1;
build(ls), build(rs), ps(x);
}
void solve() {
build(rt);
for(int j = 1; j <= n; ++ j) {
// add(rt, j, j, h[j]);
for(int i : U[j]) {
int val = (j == i + a[i] ? - h[i] : - INF);
mdf(rt, i, val);
}
int nl = max(1ll, j - b[j]), nr = j - a[j];
if(nl <= nr) {
add(rt, nl, nr, h[j]);
add(rt, nl, nr, - h[j]);
}
for(int id : G[j]) {
hans = - INF;
qry(rt, q[id].l, j);
ans[id] = max(ans[id], hans);
}
}
}
signed main() {
// fin;
n = read();
for(int i = 1; i <= n; ++ i) {
h[i] = read(), a[i] = read(), b[i] = read();
if(i + a[i] <= n) U[i + a[i]].ep(i);
if(i + b[i] + 1 <= n) U[i + b[i] + 1].ep(i);
}
T = read();
for(int i = 1; i <= T; ++ i) {
q[i].init();
G[q[i].r].ep(i);
ans[i] = -1;
}
solve();
for(int i = 1; i <= n; ++ i) h[i] = INF - h[i];
solve();
for(int i = 1; i <= T; ++ i) printf("%lld\n", ans[i]);
return 0;
}