P4433 [COCI2009-2010#1] ALADIN 题解

Problem

有一个长度为 \(n\) 初始所有元素均为为 \(0\) 的数列 \(a\)

要求支持两种操作:

  • 给定 \(L, R, A, B\),表示对于 \(\forall i\in [L, R]\)\(a_i \gets (A\times (i - L + 1)) \bmod B\)

  • 给定 \(L, R\),输出 \(\sum\limits_{i = L}^{R} a_i\)

\(1\le n\le 10^9, 1\le q\le 5\times 10^4\)

\(8s, 64MB\)

保证任何时候 \(\sum\limits_{i = 1}^{n} a_i < 2^{63} - 1\)

Sol

发现这个东西和等差数列相似,易于下传标记,考虑用线段树维护。

发现模数不固定,就不能直接加一个模数的 tag。于是考虑拆开修改的贡献,有一个常用的模数相关的 trick,就是 \(A\bmod B = A - \lfloor\frac{A}{B}\rfloor\times B\)。思考操作 \(L, R, A, B\) 会对线段树上的 \([l, r]\in [L, R]\) 的节点的 \(sum\) 会产生什么影响:\(\sum \limits_{i = L}^{R} A\times (i - L + 1) \bmod B = A\times \frac{(R - L + 1)\times (R - L + 2)}{2} - B\sum\limits_{i = L}^{R} \lfloor\frac{A(i - L + 1)}{B}\rfloor\)。前面的部分显然可以直接下传,计算。后面的部分不是很好计算,去掉 \(B\) 换一个形式,并用 \(len\) 替代 \(R - L + 1\),得到:\(\sum \limits_{i = 1}^{len} \lfloor \frac{Ai}{B} \rfloor\)。发现这就是一个类欧板子,甚至没有了板子的常数项,会类欧的可以直接跳过下面一段。


求解 \(\sum\limits_{i = 0}^{n} \lfloor\frac{Ai + C}{B}\rfloor\)

\(A = 0\) 是简单的,即 \((n + 1)\lfloor \frac{C}{B} \rfloor\)

\(A\le B \wedge C\le B\):令 \(m = \lfloor \frac{An + C}{B}\rfloor\)\(\sum\limits_{i = 0}^{n} \lfloor\frac{Ai + C}{B}\rfloor = \sum\limits_{i = 0}^{n} \sum\limits_{j = 1}^{m}[j \le \lfloor \frac{Ai + C}{B} \rfloor]\)。其中的中括号是艾弗森括号,其满足条件值为 \(1\),否则为 \(0\)。向下取整是很麻烦的,用向下取整的相关性质去掉除号:

\[\sum\limits_{j = 1}^{m} j\le \lfloor\frac{Ai + C}{B}\rfloor \]

\[\Leftrightarrow\sum \limits_{j = 0}^{m - 1} [B(j + 1) \le B\lfloor \frac{Ai + C}{B} \rfloor] \]

\[\Leftrightarrow \sum \limits_{j = 0}^{m - 1} [Bj + B - 1 < Ai + C] \]

\[\Leftrightarrow \sum \limits_{j = 0}^{m - 1} [\frac{Bj + B - C - 1}{A} < i] \]

带入得到:\(\sum\limits_{i = 0}^{n}\sum \limits_{j = 0}^{m - 1} [\frac{Bj + B - C - 1}{A} < i]\),交换 \(i, j\) 维可以得到 \(\sum \limits_{j = 0}^{m - 1}\sum\limits_{i = 0}^{n}[\frac{Bj + B - C - 1}{A} < i]\),然后发现后面部分的贡献可以直接计算,于是可以得到 \(\sum \limits_{j = 0}^{m - 1}n - \lfloor\frac{Bj + B - C - 1}{A}\rfloor\),在化一下变成 \(mn - \sum\limits_{i = 0}^{m - 1}\lfloor \frac{Bi + B - C - 1}{A} \rfloor\)。若记 \(f(A, B, C, n)\) 表示 \(\sum\limits_{i = 0}^{n} \lfloor\frac{Ai + C}{B}\rfloor\),后面的和式显然就是 \(f(B, B - C - 1, A, m - 1)\),递归计算即可。

否则有 \(\sum\limits_{i = 0}^{n} \lfloor\frac{Ai + C}{B}\rfloor \Leftrightarrow \sum\limits_{i = 0}^{n} \lfloor\frac{(A\bmod B)i + (C \bmod B)}{B}\rfloor + \frac{n(n + 1)}{2}\lfloor \frac{A}{B}\rfloor + (n + 1) \lfloor \frac{C}{B} \rfloor\),前半部分为 \(f(A\bmod B, C\bmod B, B, n)\) 继续递归即可。


具体地,在线段树上维护形如 \((a, b, t)\) 的 tag,表示覆盖的是在当前的 \([L, R]\) 中,\(\lfloor\frac{ai}{b}\rfloor\)\(i\) 是从 \(t\) 开始覆盖的,然后线段树进行区间修改和区间求和即可。

类欧的操作三的复杂度证明参考欧几里得算法,即次数为 \(\mathcal{O}(\log n)\) 的,而执行第二种操作之后再执行一次 \(a\) 就会变为 \(0\)。所以更新线段树上单个节点的复杂度为 \(\mathcal{O}(\log n)\),则总时间复杂度为 \(\mathcal{O}(q\log^2 n)\) 的,空间在离散化后可做到 \(\mathcal{O}(q)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define pb emplace_back
#define pii pair < int, int >
#define fi first
#define se second
using namespace std;
int n, m, num;
int b[100010];
struct ques {
	int opt, l, r, a, b;
} q[50010];
ll solve(ll a, ll b, ll c, ll n) {
	if(!a)
		return b / c * (n + 1);
	if(a >= c || b >= c)
		return n * (n + 1) / 2 * (a / c) + (b / c) * (n + 1) + solve(a % c, b % c, c, n);
	ll m = (a * n + b) / c;
	return m * n - solve(c, c - b - 1, a, m - 1);
}
struct Tag {
	ll a, b, t;
	Tag(ll _a = 0, ll _b = 0, ll _t = 0) {
		a = _a, b = _b, t = _t;
	}
}; 
struct segtree {
	Tag laz[400010];
	ll val[400010];
	void tag(int x, ll L, ll R, ll va, ll vb, int t) {
		L = b[L], R = b[R + 1] - 1;
		val[x] = (R - L + 2 + 2 * t) * (R - L + 1) / 2 * va - vb * (solve(va, 0, vb, R - L + 1 + t) - solve(va, 0, vb, t));
		laz[x].a = va, laz[x].b = vb, laz[x].t = t;
	}
	void down(int x, int L, int R) {
		if(laz[x].a && laz[x].b) {
			int mid = (L + R) >> 1;
			tag(x << 1, L, mid, laz[x].a, laz[x].b, laz[x].t);
			tag(x << 1 | 1, mid + 1, R, laz[x].a, laz[x].b, laz[x].t + b[mid + 1] - b[L]);
			laz[x].a = laz[x].b = laz[x].t = 0;
		}
	}
	void modify(int x, int L, int R, int l, int r, ll va, ll vb) {
		if(l <= L && R <= r)
			return tag(x, L, R, va, vb, b[L] - b[l]);
		down(x, L, R);
		int mid = (L + R) >> 1;
		if(l <= mid)
			modify(x << 1, L, mid, l, r, va, vb);
		if(r > mid)
			modify(x << 1 | 1, mid + 1, R, l, r, va, vb);
		val[x] = val[x << 1] + val[x << 1 | 1];
	}
	ll query(int x, int L, int R, int l, int r) {
		if(l <= L && R <= r)
			return val[x];
		down(x, L, R);
		int mid = (L + R) >> 1; ll res = 0;
		if(l <= mid)
			res = query(x << 1, L, mid, l, r);
		if(r > mid)
			res += query(x << 1 | 1, mid + 1, R, l, r);
		return res;
	}
} t;
int main() {
	ios :: sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	n = 0;
	for(int i = 1; i <= m; ++i) {
		cin >> q[i].opt >> q[i].l >> q[i].r;
		b[++n] = q[i].l;
		b[++n] = ++q[i].r;
		if(q[i].opt == 1)
			cin >> q[i].a >> q[i].b;
	}
	sort(b + 1, b + n + 1);
	n = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= m; ++i) {
		int l = q[i].l, r = q[i].r;
		l = lower_bound(b + 1, b + n + 1, l) - b;
		r = lower_bound(b + 1, b + n + 1, r) - b - 1;
		if(q[i].opt == 1)
			t.modify(1, 1, n - 1, l, r, q[i].a, q[i].b);
		else
			cout << t.query(1, 1, n - 1, l, r) << "\n";
	}
	return 0;
}
posted @ 2024-02-11 22:53  Pengzt  阅读(3)  评论(0编辑  收藏  举报