「KTSC 2020 R1」穿越 做题记录
此处应该有「笔记」。link
对于 \(n\le 2500\) 的分数是容易的:设 \(f_{i, j}\) 表示从 \((0,\_)\) 走到 \((j,\_)\) 分 \(i\) 段的情况下穿越障碍物的最小次数,最后求个凸包来查询。
使用线段树可以做到 \(\mathcal O(n ^ 2 \log n)\)。
我太菜了,所以对于一个大小为 \(V\times V\) 的平面的整点构成的凸包(其中不存在三个点在一条直线上)中的点数为 \(\mathcal O(V ^ {\frac 23})\)。
证明可以看这里,感觉很厉害。
所以我们可以把 \(i\) 这一维和 \(f_{i, j}\) 的值域维捆起来丢在一个平面上。
很显然,我们只关心凸包上的点,所以线段树每个点维护一个凸包,合并是对位 min。
由于凸包的点数为 \(\mathcal O(n ^ {\frac 23})\),所以时间复杂度为 \(\mathcal O(n ^ {\frac 53} \log n)\)。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define uLL unsigned LL
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb emplace_back
#define i128 __int128
const ll maxn = 2e4 + 10, mod = 998244353, M = 1e6 + 10, inf = 1e9;
template <class T>
void rd(T &x) {
char ch; ll f = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') f = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(f) x = -x;
}
ll power(ll a, ll b = mod - 2, ll p = mod) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a % p;
a = 1ll * a * a % p, b >>= 1;
} return s;
}
template <class T1, class T2>
void add(T1 &x, const T2 y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
void sub(T1 &x, const T2 y) { x = x < y? x + mod - y : x - y; }
template <class T1, class T2>
ll pls(const T1 x, const T2 y) { return x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
ll mus(const T1 x, const T2 y) { return x < y? x + mod - y : x - y; }
template <class T1, class T2>
void chkmax(T1 &x, const T2 y) { x = x < y? y : x; }
template <class T1, class T2>
void chkmin(T1 &x, const T2 y) { x = x < y? x : y; }
using namespace std;
ll n, m, l[maxn], r[maxn], h[maxn], ht;
struct point { ll x, y; point(ll X = 0, ll Y = 0) { x = X, y = Y; } };
#define Convex vector <point>
const Convex operator + (const Convex &a, const Convex &b) {
Convex c;
ll i = 0, j = 0;
while(i < a.size() || j < b.size()) {
point tmp;
if(i < a.size() && (j == b.size() || a[i].x <= b[j].x)) tmp = a[i++];
else tmp = b[j++];
if(!c.empty() && c.back().x == tmp.x && c.back().y <= tmp.y) continue;
while(c.size() > 1) {
point bk = c.back(), sd = c[c.size() - 2];
if(1ll * (tmp.x - bk.x) * (bk.y - sd.y)
>= 1ll * (tmp.y - bk.y) * (bk.x - sd.x)) c.pop_back();
else break;
}
c.pb(tmp);
} return c;
}
const Convex mov(const Convex &a, const ll dx, const ll dy) {
Convex b = a;
for(point &tmp: b) tmp.x += dx, tmp.y += dy;
return b;
}
struct SGT {
Convex mn[maxn << 2], tag[maxn << 2]; ll add[maxn << 2];
void addtag(ll p, ll k, const Convex &tg) {
if(k) mn[p] = mov(mn[p], 0, k), tag[p] = mov(tag[p], 0, k);
mn[p] = mn[p] + tg, tag[p] = tag[p] + tg, add[p] += k;
}
void pushdown(ll p) {
if(tag[p].empty()) return;
addtag(p << 1, add[p], tag[p]), addtag(p << 1|1, add[p], tag[p]);
Convex().swap(tag[p]), add[p] = 0;
}
void modify(ll p, ll l, ll r, ll ql, ll qr) {
if(ql <= l && r <= qr) return addtag(p, 1, {});
pushdown(p); ll mid = l + r >> 1;
if(ql <= mid) modify(p << 1, l, mid, ql, qr);
if(mid < qr) modify(p << 1|1, mid + 1, r, ql, qr);
mn[p] = mn[p << 1] + mn[p << 1|1];
}
} tr;
Convex res;
void init(ll N, ll M, vector <ll> Y1, vector <ll> Y2) {
n = N, m = M;
h[++ht] = 1, h[++ht] = M + 1;
for(ll i = 1; i <= n; i++) {
l[i] = Y1[i - 1] + 1, r[i] = Y2[i - 1] + 1;
h[++ht] = l[i], h[++ht] = r[i] + 1;
}
sort(h + 1, h + 1 + ht);
ht = unique(h + 1, h + 1 + ht) - h - 2;
for(ll i = 1; i <= 4 * ht; i++)
tr.mn[i] = {point(0, 0)};
for(ll i = 1; i <= n; i++) {
l[i] = lower_bound(h + 1, h + 1 + ht, l[i]) - h;
r[i] = upper_bound(h + 1, h + 1 + ht, r[i]) - h - 1;
tr.modify(1, 1, ht, l[i], r[i]);
tr.addtag(1, 0, mov(tr.mn[1], 1, 0));
}
res = tr.mn[1];
}
LL minimize(ll A, ll B) {
ll lo = 0, hi = res.size() - 2;
while(lo <= hi) {
ll mid = lo + hi >> 1;
if(1ll * A * res[mid].x + 1ll * B * res[mid].y
> 1ll * A * res[mid + 1].x + 1ll * B * res[mid + 1].y)
lo = mid + 1;
else hi = mid - 1;
}
return 1ll * A * res[lo].x + 1ll * B * res[lo].y;
}