「JOISC 2019 Day3」穿越时空 Bitaro(差分+线段树维护分段函数):

https://loj.ac/problem/3038

题解:

只考虑从左往右是怎么做的,从右往左同理。

如果是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);
		}
	}
}
posted @ 2020-03-10 17:08  Cold_Chair  阅读(256)  评论(0编辑  收藏  举报