【BZOJ3878】【JSOI2014】奇怪的计算器

【题目链接】

【思路要点】

  • 首先我们来考虑只有加减怎么做,考虑一下最终的函数图像,它应该始终由一条处于中间的斜率为1的线段与左右各一条斜率为0的线段构成。简单描述一下这个图像,我们可以在\(O(N+Q)\)的时间内解决只有加减的情况。
  • 考虑加入乘法,由于乘法同样只和数值有关,而与坐标无关,因此最终函数图像同样始终由一条处于中间的斜率为\(x\)的线段与左右各一条斜率为0的线段构成。沿用上面的做法,我们依然可以在\(O(N+Q)\)的时间内解决只有加、减和乘的情况。
  • 在加入\(@\)运算前,我们先分析一下每种操作对某一段函数图像\(y=k(x-l)+b(x\in[l,r])\)的影响。
  • 1、\(+a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=k(x-l)+(b+a)(x\in[l,r])\)。
  • 2、\(-a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=k(x-l)+(b-a)(x\in[l,r])\)。
  • 3、\(*a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=(k*a)(x-l)+(b*a)(x\in[l,r])\)。
  • 4、\(@a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=(k+a)(x-l)+(b+l*a)(x\in[l,r])\)。
  • 可以看到,\(@\)运算会使斜率进行加法,因此最终的函数可能分为\(O(N)\)段。
  • 用线段树维护分段函数集合,上面的各种修改线段树均能够支持。
  • 注意到在任何时刻,最终函数为单调函数,因此可以在修改前检查最左/右侧可能产生越界的函数,对其单独进行修改,这样同样避免了可能爆long long的问题。
  • 询问时在得到的分段函数上二分即可。
  • 时间复杂度\(O(NLogN+QLogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MAXP = 400005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
void getopt(char &x) {
	x = getchar();
	while (x != '+' && x != '-' && x != '*' && x != '@')
		x = getchar();
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		long long indexadd, indexfrm, indexmul;
		long long slopeadd, slopemul;
	} a[MAXP];
	struct Line {
		int from, to;
		long long index, slope;
	} val[MAXN * 2];
	int root, size, n, nl, nr, vl, vr;
	void pushdown(int root) {
		if (a[root].lc) {
			int now = a[root].lc;
			a[now].indexadd *= a[root].indexmul;
			a[now].indexadd += a[root].indexadd;
			a[now].indexfrm *= a[root].indexmul;
			a[now].indexfrm += a[root].indexfrm;
			a[now].indexmul *= a[root].indexmul;
			a[now].slopeadd *= a[root].slopemul;
			a[now].slopeadd += a[root].slopeadd;
			a[now].slopemul *= a[root].slopemul;
		}
		if (a[root].rc) {
			int now = a[root].rc;
			a[now].indexadd *= a[root].indexmul;
			a[now].indexadd += a[root].indexadd;
			a[now].indexfrm *= a[root].indexmul;
			a[now].indexfrm += a[root].indexfrm;
			a[now].indexmul *= a[root].indexmul;
			a[now].slopeadd *= a[root].slopemul;
			a[now].slopeadd += a[root].slopeadd;
			a[now].slopemul *= a[root].slopemul;
		}
		a[root].indexadd = 0;
		a[root].indexfrm = 0;
		a[root].indexmul = 1;
		a[root].slopeadd = 0;
		a[root].slopemul = 1;
	}
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].indexadd = 0;
		a[root].indexfrm = 0;
		a[root].indexmul = 1;
		a[root].slopeadd = 0;
		a[root].slopemul = 1;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x, int l, int r) {
		n = x * 2 + 1;
		nl = nr = x + 1;
		vl = l, vr = r;
		val[x + 1] = (Line) {vl, vr, vl, 1};
		build(root, 1, n);
	}
	void inc(int root, int l, int r, int ql, int qr, int x) {
		if (l == ql && r == qr) {
			a[root].indexadd += x;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) inc(a[root].lc, l, mid, ql, min(mid, qr), x);
		if (mid + 1 <= qr) inc(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void times(int root, int l, int r, int ql, int qr, int x) {
		if (l == ql && r == qr) {
			a[root].indexadd *= x;
			a[root].indexfrm *= x;
			a[root].indexmul *= x;
			a[root].slopeadd *= x;
			a[root].slopemul *= x;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) times(a[root].lc, l, mid, ql, min(mid, qr), x);
		if (mid + 1 <= qr) times(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void modify(int root, int l, int r, int ql, int qr, int x) {
		if (l == ql && r == qr) {
			a[root].indexfrm += x;
			a[root].slopeadd += x;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr), x);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void clarify(int root, int l, int r, int pos) {
		if (l == r) {
			val[l].index *= a[root].indexmul;
			val[l].index += a[root].indexadd;
			val[l].index += a[root].indexfrm * val[l].from;
			val[l].slope *= a[root].slopemul;
			val[l].slope += a[root].slopeadd;
			a[root].indexadd = 0;
			a[root].indexfrm = 0;
			a[root].indexmul = 1;
			a[root].slopeadd = 0;
			a[root].slopemul = 1;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= pos) clarify(a[root].lc, l, mid, pos);
		else clarify(a[root].rc, mid + 1, r, pos);
	}
	long long calc(int pos, int x) {
		return (x - val[pos].from) * val[pos].slope + val[pos].index;
	}
	void inc(int x) {
		clarify(root, 1, n, nr);
		if (calc(nr, val[nr].to) + x <= vr) {
			inc(root, 1, n, nl, nr, x);
			return;
		}
		while (nr >= nl) {
			clarify(root, 1, n, nr);
			if (calc(nr, val[nr].from) + x >= vr) nr--;
			else {
				int l = val[nr].from, r = val[nr].to + 1;
				while (l < r) {
					int mid = (l + r) / 2;
					if (calc(nr, mid) + x <= vr) l = mid + 1;
					else r = mid;
				}
				val[nr].to = l - 1;
				inc(root, 1, n, nl, nr, x);
				clarify(root, 1, n, ++nr);
				val[nr] = (Line) {l, vr, vr, 0};
				return;
			}
		}
		clarify(root, 1, n, ++nr);
		val[nr] = (Line) {vl, vr, vr, 0};
	}
	void dec(int x) {
		clarify(root, 1, n, nl);
		if (calc(nl, val[nl].from) - x >= vl) {
			inc(root, 1, n, nl, nr, -x);
			return;
		}
		while (nr >= nl) {
			clarify(root, 1, n, nl);
			if (calc(nl, val[nl].to) - x <= vl) nl++;
			else {
				int l = val[nl].from - 1, r = val[nl].to;
				while (l < r) {
					int mid = (l + r + 1) / 2;
					if (calc(nl, mid) - x >= vl) r = mid - 1;
					else l = mid;
				}
				val[nl].index = calc(nl, l + 1);
				val[nl].from = l + 1;
				inc(root, 1, n, nl, nr, -x);
				clarify(root, 1, n, --nl);
				val[nl] = (Line) {vl, l, vl, 0};
				return;
			}
		}
		clarify(root, 1, n, --nl);
		val[nl] = (Line) {vl, vr, vl, 0};
	}
	void times(int x) {
		clarify(root, 1, n, nr);
		if (calc(nr, val[nr].to) * x <= vr) {
			times(root, 1, n, nl, nr, x);
			return;
		}
		while (nr >= nl) {
			clarify(root, 1, n, nr);
			if (calc(nr, val[nr].from) * x >= vr) nr--;
			else {
				int l = val[nr].from, r = val[nr].to + 1;
				while (l < r) {
					int mid = (l + r) / 2;
					if (calc(nr, mid) * x <= vr) l = mid + 1;
					else r = mid;
				}
				val[nr].to = l - 1;
				times(root, 1, n, nl, nr, x);
				clarify(root, 1, n, ++nr);
				val[nr] = (Line) {l, vr, vr, 0};
				return;
			}
		}
		clarify(root, 1, n, ++nr);
		val[nr] = (Line) {vl, vr, vr, 0};
	}
	void modify(long long x) {
		clarify(root, 1, n, nr);
		if (calc(nr, val[nr].to) + val[nr].to * x <= vr) {
			times(root, 1, n, nl, nr, x);
			return;
		}
		while (nr >= nl) {
			clarify(root, 1, n, nr);
			if (calc(nr, val[nr].from) + val[nr].from * x >= vr) nr--;
			else {
				int l = val[nr].from, r = val[nr].to + 1;
				while (l < r) {
					int mid = (l + r) / 2;
					if (calc(nr, mid) + mid * x <= vr) l = mid + 1;
					else r = mid;
				}
				val[nr].to = l - 1;
				modify(root, 1, n, nl, nr, x);
				clarify(root, 1, n, ++nr);
				val[nr] = (Line) {l, vr, vr, 0};
				return;
			}
		}
		clarify(root, 1, n, ++nr);
		val[nr] = (Line) {vl, vr, vr, 0};
	}
	void finish() {
		for (int i = nl; i <= nr; i++)
			clarify(root, 1, n, i);
	}
	int query(int x) {
		int l = nl, r = nr;
		while (true) {
			int mid = (l + r) / 2;
			if (val[mid].from <= x && val[mid].to >= x) return calc(mid, x);
			if (val[mid].from <= x) l = mid + 1;
			else r = mid - 1; 
		}
	}
} ST;
int n, l, r, q;
int main() {
	read(n), read(l), read(r);
	ST.init(n, l, r);
	for (int i = 1; i <= n; i++) {
		char opt; int x;
		getopt(opt), read(x);
		if (opt == '+') ST.inc(x);
		if (opt == '-') ST.dec(x);
		if (opt == '*') ST.times(x);
		if (opt == '@') ST.modify(x);
	}
	ST.finish();
	read(q);
	for (int i = 1; i <= q; i++) {
		int x; read(x);
		writeln(ST.query(x));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值