「JOISC 2019 Day3」穿越时空 Bitaro(差分+线段树维护分段函数):
题解:
只考虑从左往右是怎么做的,从右往左同理。
如果是x->y,那么最优的方案肯定是一直往右走。
考虑从x->x+1,时间会加1,这让我们非常不爽。
不妨把\(l[i]-=i,r[i]-=i\),这样就不用考虑走边带来的时间+1(硬要考虑也是行的)。
于是每题变成,一个矩阵,第\(i\)列可以走\([l[i],r[i])\)行,列与列之间向上走没有代价,向下走一个有一个代价。
当只经过一列时,不难发现代价和终点都是三段关于起点的一次函数。
事实上,通过归纳,可以得到,当我们经过一个区间的列时,代价和终点都是三段分段函数。
那么我们套个线段树,线段树上就暴力维护这个分段函数就好了,我写的比较丑,有一个3*3=9的大常数。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 3e5 + 5;
const ll inf = 2e9;
struct P {
ll x, y;
P(ll _x = 0, ll _y = 0) {
x = _x, y = _y;
}
};
bool operator == (P a, P b) {
return a.x == b.x && a.y == b.y;
}
ll val(P a, ll b) {
return a.x * b + a.y;
}
struct Q {
P a, b, c;
};
int n, q, op;
ll l[N], r[N], x, y, z, w;
#define i0 i + i
#define i1 i + i + 1
Q f[N * 4][3], g[N * 4][3]; int pl, pr;
Q d[10]; int d0;
void mer(Q a, Q b) {
if(a.a.x > a.a.y) return;
if(b.a.x > b.a.y) return;
if(a.b.x == 0) {
if(!(a.b.y >= b.a.x && a.b.y <= b.a.y)) return;
if(b.b.x == 0) {
a.c.y = a.c.y + val(b.c, a.b.y);
a.b = b.b;
d[++ d0] = a;
} else {
d[++ d0] = a;
}
} else {
ll l = max(a.a.x, b.a.x), r = min(a.a.y, b.a.y);
if(l > r) return;
a.a = P(l, r);
if(b.b.x == 0) {
a.b = b.b;
a.c = b.c;
d[++ d0] = a;
} else {
d[++ d0] = a;
}
}
}
Q d2[10];
void dod() {
fo(i, 1, d0) d2[i] = d[i];
int D = d0; d0 = 0;
fo(i, 1, D) {
if(!d0 || !(d2[i].b == d[d0].b && d2[i].c == d[d0].c)) {
d[++ d0] = d2[i];
} else {
d[d0].a.y = d2[i].a.y;
}
}
}
void upd(Q *a, Q *b, Q *c) {
d0 = 0;
fo(i, 0, 2) fo(j, 0, 2) mer(b[i], c[j]);
dod();
fo(i, 1, d0) a[i - 1] = d[i];
fo(i, d0, 2) a[i].a = P(1, 0);
}
void add(int i, int x, int y) {
if(x == y) {
int u = l[x] - x, v = r[x] - x - 1;
f[i][0] = (Q) {P(-inf, u - 1), P(0, u), P(0, 0)};
f[i][1] = (Q) {P(u, v), P(1, 0), P(0, 0)};
f[i][2] = (Q) {P(v + 1, inf), P(0, v), P(1, -v)};
u = l[x] + x, v = r[x] + x - 1;
g[i][0] = (Q) {P(-inf, u - 1), P(0, u), P(0, 0)};
g[i][1] = (Q) {P(u, v), P(1, 0), P(0, 0)};
g[i][2] = (Q) {P(v + 1, inf), P(0, v), P(1, -v)};
return;
}
int m = x + y >> 1;
if(pl <= m) add(i0, x, m); else add(i1, m + 1, y);
upd(f[i], f[i0], f[i1]);
upd(g[i], g[i1], g[i0]);
}
Q px[3]; int py;
void ft(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
upd(px, px, py ? g[i] : f[i]);
return;
}
int m = x + y >> 1;
if(py) ft(i1, m + 1, y), ft(i0, x, m); else
ft(i0, x, m), ft(i1, m + 1, y);
}
int main() {
scanf("%d %d", &n, &q);
n --;
fo(i, 1, n) {
scanf("%d %d", &l[i], &r[i]);
pl = pr = i, add(1, 1, n);
}
fo(ii, 1, q) {
scanf("%d %lld %lld %lld", &op, &x, &y, &z);
if(op == 1) {
l[x] = y, r[x] = z;
pl = pr = x;
add(1, 1, n);
} else {
scanf("%lld", &w);
px[0] = (Q) {P(-inf, inf), P(1, 0), P(0, 0)};
px[1].a = px[2].a = P(1, 0);
ll ans = 0;
if(x <= z) {
y -= x, w -= z;
pl = x, pr = z - 1; py = 0; ft(1, 1, n);
} else {
y += x - 1, w += z - 1;
pl = z, pr = x - 1; py = 1; ft(1, 1, n);
}
fo(j, 0, 2) if(y >= px[j].a.x && y <= px[j].a.y) {
ans += val(px[j].c, y);
y = val(px[j].b, y);
break;
}
ans += max(0ll, y - w);
pp("%lld\n", ans);
}
}
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址