MX galaxy Day6
walk
最大值和最小值肯定要划分到同一个集合,设这个集合为 \(A\) ,另一个集合为 \(B\) ,则代价为
\(\max_{b\in B}\{|max-b|, |min-b| \}\) ,则另一个集合越小越优,而边角的那一个点是必选的,只留那一个就好了。
所以答案就是右上角单独一个集合和左下角单独一个集合取更优。
color
分三种情况,两条横的,两条竖的,一横一竖的。
前两种是好做的,现在考虑第三种情况。
枚举横纵延长线的交点,用前后缀优化就好了。
(本人没有写这种做法)
点击查看
#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 _ = 1500 + 7;
typedef long long ll;
typedef std::pair<int, int> PII;
int n, len, ans, st, ed, al[_], ar[_], au[_], ad[_];
int cn, cx, rn, rx;
char s[_][_]; bool c[_][_], r[_][_];
void upd(int x, int& a, int& b) {
if (x >= a) b = a, a = x;
else if (x >= b) b = x;
}
void Dwn(int x, int y) {
if (x == n + 1 or s[x][y] == 'X') return upd(len, cn, cx);
c[x][y] = true, ++len;
Dwn(x + 1, y);
}
void Rgh(int x, int y) {
if (y == n + 1 or s[x][y] == 'X') return ed = y, upd(len, rn, rx);
r[x][y] = true, ++len;
Rgh(x, y + 1);
al[y] = std::max(al[y], y - st + 1), ar[y] = std::max(ar[y], ed - y);
}
int main() {
scanf("%d", & n);
lep(i, 1, n) scanf("%s", s[i] + 1);
lep(i, 1, n) lep(j, 1, n) if (s[i][j] == '.') {
if (!c[i][j]) len = 0, Dwn(i, j), ans = std::max(ans, len / 2);
if (!r[i][j]) len = 0, st = j, Rgh(i, j), ans = std::max(ans, len / 2),
au[i] = std::max(au[i], len), ad[i] = au[i];
}
ans = std::max(ans, std::min(cn, cx)), ans = std::max(ans, std::min(rn, rx));
lep(i, 2, n) al[i] = std::max(al[i], al[i - 1]), au[i] = std::max(au[i], au[i - 1]);
rep(i, n - 1, 1) ar[i] = std::max(ar[i], ar[i + 1]), ad[i] = std::max(ad[i], ad[i + 1]);
lep(i, 1, n) lep(j, 1, n) if (s[i][j] == '.') {
int R = std::max(std::max(au[i - 1], al[j - 1]), ar[j + 1]);
lep(k, i, n) {
if (s[k][j] == 'X') break;
else ans = std::max(ans, std::min(k - i + 1, std::max(R, ad[k + 1])));
}
}
printf("%d\n", ans);
return 0;
}
orz
我们将同一个人在不同排列中的位置连 \(0\) 边,每个位置向其后缀连 \(1\) 边。
那么我们想要找到 \(x\) 等价类 到 \(y\) 等价类 的最短路。
但是这样转化是完全倒闭的,我们可以对于每个位置 \((i, j)\) 处理出其跳到 \(k\) 列时最靠前的位置。
这样一定是最优的。
但这样复杂度接受不了,我们可以倍增。
具体的,处理出 \(to[i, k, p, j]\) 表示 \((i, j)\) 这个位置跳 \(2^p\) 步到第 \(k\) 行最靠前的位置。
注意 \(\boldsymbol{内存连续性优化}\) ,将 \(j\) 这一维放到最后,可以快 \(5\) 倍左右。
点击查看
#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 _ = 2e5 + 7;
typedef long long ll;
int n, m, q, a[5][_], b[5][_], to[5][5][21][_]; //i k p j
int nw[5], tmp[5];
int main() {
scanf("%d%d", & n, & m);
lep(i, 0, m - 1) lep(j, 1, n) scanf("%d", a[i] + j), b[i][a[i][j]] = j;
lep(k, 0, m - 1) lep(i, 0, m - 1) {
rep(j, n, 1) {
to[i][k][0][j] = b[k][a[i][j]];
if (j != n) to[i][k][0][j] = std::min(to[i][k][0][j], to[i][k][0][j + 1]);
}
}
lep(p, 1, 20) {
lep(i, 0, m - 1) lep(k, 0, m - 1) lep(j, 1, n) {
to[i][k][p][j] = n + 1;
lep(t, 0, m - 1) to[i][k][p][j] = std::min(to[i][k][p][j], to[t][k][p - 1][to[i][t][p - 1][j]]);
}
}
scanf("%d", & q); int x, y;
while (q--) {
scanf("%d%d", & x, & y); int ans = 0; bool legal = false;
lep(i, 0, m - 1) nw[i] = b[i][x];
lep(i, 0, m - 1) if (nw[i] <= b[i][y]) { puts("1"); legal = true; break; }
if (legal) continue;
rep(p, 20, 0) { bool flag = true;
lep(i, 0, m - 1) {
tmp[i] = nw[i];
lep(j, 0, m - 1) tmp[i] = std::min(tmp[i], to[j][i][p][nw[j]]);
if (tmp[i] <= b[i][y]) { flag = false, legal = true; break; }
}
if (flag) {
if (p == 20) break;
lep(i, 0, m - 1) nw[i] = tmp[i];
ans += (1 << p);
}
}
if (legal) printf("%d\n", ans + 2);
else puts("-1");
}
return 0;
}
flight
注意到一条转移路径除了终点外 \(b\) 是单调下降的。
这为我们的转移规定了顺序,我们按照 \(b\) 降序处理,但是路径信息我们很难做,考虑点分树。
推一下式子, \(dp_u = \min\{dp_v + a_v + b_v \times dis(rt, v) + b_v\times dis(rt, u) \}\) 。
变成了斜率优化的形式,用 李超线段树 维护。
具体的,每考虑到一个点,先用其点分树上的所有祖先更新出答案,再把自己的直线插入祖先中。
但是,终点可能 \(b\) 并不是递减的,再对每一个点查询一遍就好了。
注意到点分树中在同一个子树内一定不优,会被更早的更新掉,所以没有影响。
李超线段树 \(TLE\) 了可以把递归改成迭代。
点击查看
#include <bits/stdc++.h>
std::vector<long long> travel(std::vector<long long> A, std::vector<int> Bi, std::vector<int> U, std::vector<int> V, std::vector<int> W);
#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;
typedef long long ll;
const ll inf = 4e18;
int n, pr[_], dep[_], dfn[_], idx, fa[_], st[_][21], lg[_], id[_];
ll a[_], b[_], len[_], dp[_];
int sz[_], tmp[_], Rt; bool vis[_];
int seg[_ * 600], ls[_ * 600], rs[_ * 600], tot, cnt, rt[_]; ll K[_ * 20], B[_ * 20];
std::vector <ll> av; int AV;
std::vector <int> e[_], g[_];
std::vector <ll> ans;
void getrt(int u, int f, int total) {
sz[u] = 1, tmp[u] = 0;
for (int v : e[u]) if (!vis[v] and v != f)
getrt(v, u, total), sz[u] += sz[v], tmp[u] = std::max(tmp[u], sz[v]);
tmp[u] = std::max(tmp[u], total - sz[u]);
if (!Rt or tmp[u] < tmp[Rt]) Rt = u;
}
int solve(int u, int total) {
Rt = 0, getrt(u, 0, total), vis[Rt] = true; int t = Rt;
for (int v : e[Rt]) if (!vis[v]) pr[solve(v, total - tmp[v])] = t;
return t;
}
void init(int u, int f, ll d) {
len[u] = d, dep[u] = dep[fa[u] = f] + 1, dfn[u] = ++idx, st[idx][0] = u;
lep(k, 0, (int)e[u].size() - 1) { int v = e[u][k], w = g[u][k];
if (v != f) init(v, u, d + w);
}
}
inline int upd(int x, int y) { return dep[x] < dep[y] ? x : y; }
inline int lca(int x, int y) {
if (dfn[x] > dfn[y]) std::swap(x, y);
int k = lg[dfn[y] - dfn[x] + 1], p = upd(st[dfn[x]][k], st[dfn[y] - (1 << k) + 1][k]);
return p == x ? x : fa[p];
}
inline ll Dis(int x, int y) { return len[x] - 2 * len[lca(x, y)] + len[y]; }
void Init() {
init(1, 0, 0), solve(1, n);
lep(j, 1, 20) lep(i, 1, n - (1 << j) + 1)
st[i][j] = upd(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
dp[1] = 0;
lep(i, 2, n) lg[i] = lg[i >> 1] + 1, dp[i] = a[1] + b[1] * Dis(1, i);
lep(i, 1, n) {
int j = i;
while (j) av.push_back(Dis(i, j)), j = pr[j];
}
std::sort(av.begin(), av.end()), av.erase(std::unique(av.begin(), av.end()), av.end()); AV = av.size();
}
inline ll Val(int x, int d) { return K[x] * av[d - 1] + B[x]; }
inline int upd(int x, int y, int d) { return (!x or !y) ? (x | y) : Val(x, d) < Val(y, d) ? x : y; }
#define md ((s + t) >> 1)
void mdy(int s, int t, int v, int* p) {
while (true) {
if (!v) break; if (!*p) *p = ++tot; int& u = seg[*p];
if (upd(u, v, md) == v) std::swap(u, v);
if (s == t or !v) break;
if (upd(u, v, s) == v) t = md, p = &ls[*p];
else if (upd(u, v, t) == v) s = md + 1, p = &rs[*p];
else break;
}
}
int qry(int s, int t, int d, int p) { int res = 0;
while (true) {
if (!seg[p]) break;
res = upd(res, seg[p], d);
if (s == t) break;
d <= md ? (t = md, p = ls[p]) : (s = md + 1, p = rs[p]);
}
return res;
}
#undef md
void Ins(int x, int u) {
K[++cnt] = b[u], B[cnt] = a[u] + dp[u] + b[u] * Dis(x, u);
mdy(1, AV, cnt, &rt[x]);
}
inline int ID(ll x) { return std::lower_bound(av.begin(), av.end(), x) - av.begin() + 1; }
ll Calc(int x, int u) { int p = ID(Dis(x, u)); return Val(qry(1, AV, p, rt[x]), p); }
std::vector<long long> travel(std::vector<long long> A, std::vector<int> Bi, std::vector<int> U, std::vector<int> V, std::vector<int> W) {
n = A.size(); int u, v, w; B[0] = inf;
lep(i, 1, n) a[i] = A[i - 1], b[i] = Bi[i - 1], id[i] = i;
dp[1] = 0;
lep(i, 2, n) u = U[i - 2], v = V[i - 2], w = W[i - 2], ++u, ++v,
e[u].push_back(v), g[u].push_back(w), e[v].push_back(u), g[v].push_back(w);
Init();
std::sort(id + 1, id + 1 + n, [](const int&x, const int&y) { return b[x] > b[y]; });
lep(j, 1, n) { u = id[j], v = u;
while (v) dp[u] = std::min(dp[u], Calc(v, u)), v = pr[v];
v = u;
while (v) Ins(v, u), v = pr[v];
}
lep(j, 1, n) { u = id[j], v = u;
while (v) dp[u] = std::min(dp[u], Calc(v, u)), v = pr[v];
}
lep(i, 2, n) ans.push_back(dp[i]);
return ans;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

结论,二分,倍增,斜率优化+点分树
浙公网安备 33010602011771号