【Gym-101955E/2018ICPC沈阳E】The Kouga Ninja Scrolls(曼哈顿距离转切比雪夫距离+大力分类讨论线段树)
题目链接:https://vjudge.net/problem/Gym-101955E
题目大意
在无限宽广的二维平面上,分布着 \(n\) 个忍者,他们各自的派别,有 \(m\) 个操作,共 \(3\) 种:
-
将编号为 \(k\) 的忍者的位置变为 \((x'+x, y'+y)\)。
-
将编号为 \(k\) 的忍者的派别变为 \(c\)。
-
询问编号在 \([l,r]\) 之间的忍者中,派别不同的最大的曼哈顿距离。
思路
询问最大的曼哈顿距离转化为切比雪夫距离模板题:[USACO04OPEN]Cave Cows 3。
先无视掉派别不同这个信息,这样的话,开两棵线段树,维护 \(x+y\) 的最大值最小值和 \(x-y\) 的最大值最小值即可,每次查询区间 \([l,r]\) 的 \(x+y\) 的最大值最小值和 \(x-y\) 的最大值最小值。
那么答案就是 \(max((x+y)_{max}-(x+y)_{min}, (x-y)_{max}-(x-y)_{min})\) 了。
若查询到的节点的最大值最小值属于不同的派别,很幸运地得到了答案。
如果非常不幸,现在要求派别不同,于是开始在线段树上大力分类讨论:
-
最大值一定是值较大的,因此将 \(fi_max\) 设置为最大值值较大的那一个的最大值。
-
先假设次大值一定存在,设成派别为 \(0\),值为无穷小的次大值。(注意派别的范围为 \(1 \sim n\),因此可以这样设置)
-
如果左右儿子派别不同的话,就赋成最大值较小的那一个的最大值。
-
若此时左儿子的次大值的值大于当前次大值的值且左儿子次大值的派别与当前最大值的派别不同,则用左儿子的次大值来覆盖当前的次大值。
-
若此时右儿子的次大值的值大于当前次大值的值且右儿子次大值的派别与当前最大值的派别不同,则用右儿子的次大值来覆盖当前的次大值。
在查询时先进行派别判断,再查询 \((x+y)_{se\_max}-(x+y)_{min}\),\((x+y)_{max}-(x+y)_{se\_min}\),\((x-y)_{se\_max}-(x-y)_{min}\),\((x-y)_{max}-(x-y)_{se\_min}\),求最大值即可。
花絮
在模拟时有想到用吉老师线段树来维护一个最大值次大值来操作,遍历知识点,但不知道转化成切比雪夫距离,然后GG了。
在补题时看到公式,感觉似曾相识,掏出板子题,发现自己完 全 忘 记。
事实上分类讨论还是很难写的,在没有想到用覆盖的方法写之前疯狂WA2,重写才行。
AC代码
#include <bits/stdc++.h>
#define ll long long
#define pil pair<int, ll>
#define llinf 0x3f3f3f3f3f3f3f3f
#define mp make_pair
using namespace std;
const int MAXN = 1e5 + 5;
pil a1[MAXN], a2[MAXN];
struct node {
int l, r;
pil fi_max, se_max;
pil fi_min, se_min;
node() {
l = 0, r = 0;
fi_max = se_max = mp(0, -llinf);
fi_min = se_min = mp(0, llinf);
}
};
class JLS {
public:
node T[MAXN << 2];
inline node merge(const node &L, const node &R) {
node ans;
ans.l = L.l, ans.r = R.r;
// max
if (L.fi_max.second > R.fi_max.second) ans.fi_max = L.fi_max;
else ans.fi_max = R.fi_max;
if (L.fi_max.first != R.fi_max.first) {
if (L.fi_max.second <= R.fi_max.second) ans.se_max = L.fi_max;
else ans.se_max = R.fi_max;
}
if (L.se_max.second > ans.se_max.second && L.se_max.first != ans.fi_max.first) {
ans.se_max = L.se_max;
}
if (R.se_max.second > ans.se_max.second && R.se_max.first != ans.fi_max.first) {
ans.se_max = R.se_max;
}
// min
if (L.fi_min.second < R.fi_min.second) ans.fi_min = L.fi_min;
else ans.fi_min = R.fi_min;
if (L.fi_min.first != R.fi_min.first) {
if (L.fi_min.second >= R.fi_min.second) ans.se_min = L.fi_min;
else ans.se_min = R.fi_min;
}
if (L.se_min.second < ans.se_min.second && L.se_min.first != ans.fi_min.first) {
ans.se_min = L.se_min;
}
if (R.se_min.second < ans.se_min.second && R.se_min.first != ans.fi_min.first) {
ans.se_min = R.se_min;
}
return ans;
}
void update1(int rt, int pos) {
if (T[rt].l == T[rt].r) {
T[rt].fi_max = a1[T[rt].l], T[rt].se_max = mp(0, -llinf);
T[rt].fi_min = a1[T[rt].l], T[rt].se_min = mp(0, llinf);
return;
}
int mid = (T[rt].l + T[rt].r) >> 1;
if (pos <= mid) update1(rt << 1, pos);
else update1(rt << 1 | 1, pos);
T[rt] = merge(T[rt << 1], T[rt << 1 | 1]);
}
void update2(int rt, int pos) {
if (T[rt].l == T[rt].r) {
T[rt].fi_max = a2[T[rt].l], T[rt].se_max = mp(0, -llinf);
T[rt].fi_min = a2[T[rt].l], T[rt].se_min = mp(0, llinf);
return;
}
int mid = (T[rt].l + T[rt].r) >> 1;
if (pos <= mid) update2(rt << 1, pos);
else update2(rt << 1 | 1, pos);
T[rt] = merge(T[rt << 1], T[rt << 1 | 1]);
}
void build1(int rt, int l, int r) {
T[rt].l = l, T[rt].r = r;
if (l == r) {
T[rt].fi_max = a1[l], T[rt].se_max = mp(0, -llinf);
T[rt].fi_min = a1[l], T[rt].se_min = mp(0, llinf);
return;
}
int mid = (l + r) >> 1;
build1(rt << 1, l, mid), build1(rt << 1 | 1, mid + 1, r);
T[rt] = merge(T[rt << 1], T[rt << 1 | 1]);
}
void build2(int rt, int l, int r) {
T[rt].l = l, T[rt].r = r;
if (l == r) {
T[rt].fi_max = a2[l], T[rt].se_max = mp(0, -llinf);
T[rt].fi_min = a2[l], T[rt].se_min = mp(0, llinf);
return;
}
int mid = (l + r) >> 1;
build2(rt << 1, l, mid), build2(rt << 1 | 1, mid + 1, r);
T[rt] = merge(T[rt << 1], T[rt << 1 | 1]);
}
node query(int rt, int L, int R) {
if (L <= T[rt].l && T[rt].r <= R) return T[rt];
int mid = (T[rt].l + T[rt].r) >> 1;
if (R <= mid) return query(rt << 1, L, R);
else if (L > mid) return query(rt << 1 | 1, L, R);
else {
node ansl = query(rt << 1, L, R), ansr = query(rt << 1 | 1, L, R);
return merge(ansl, ansr);
}
}
} tree1, tree2;
int main() {
int T;
scanf("%d", &T);
int kass = 1;
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
ll x, y;
int c;
scanf("%lld%lld%d", &x, &y, &c);
a1[i].first = c, a1[i].second = x + y;
a2[i].first = c, a2[i].second = x - y;
}
tree1.build1(1, 1, n), tree2.build2(1, 1, n);
printf("Case #%d:\n", kass++);
while (m--) {
int opt;
scanf("%d", &opt);
if (opt == 1) {
int k;
ll x, y;
scanf("%d%lld%lld", &k, &x, &y);
a1[k].second += x + y, a2[k].second += x - y;
tree1.update1(1, k), tree2.update2(1, k);
} else if (opt == 2) {
int k, c;
scanf("%d%d", &k, &c);
a1[k].first = c, a2[k].first = c;
tree1.update1(1, k), tree2.update2(1, k);
} else {
int l, r;
scanf("%d%d", &l, &r);
if (l == r) {
printf("0\n");
} else {
ll res = -1;
node ans1 = tree1.query(1, l, r);
if (ans1.fi_max.first != ans1.fi_min.first)
res = max(res, ans1.fi_max.second - ans1.fi_min.second);
if (ans1.fi_max.first != ans1.se_min.first)
res = max(res, ans1.fi_max.second - ans1.se_min.second);
if (ans1.se_max.first != ans1.fi_min.first)
res = max(res, ans1.se_max.second - ans1.fi_min.second);
node ans2 = tree2.query(1, l, r);
if (ans2.fi_max.first != ans2.fi_min.first)
res = max(res, ans2.fi_max.second - ans2.fi_min.second);
if (ans2.fi_max.first != ans2.se_min.first)
res = max(res, ans2.fi_max.second - ans2.se_min.second);
if (ans2.se_max.first != ans2.fi_min.first)
res = max(res, ans2.se_max.second - ans2.fi_min.second);
if (res == -1) printf("0\n");
else printf("%lld\n", res);
}
}
}
}
}