CodeForces 1609G A Stroll Around the Matrix

洛谷传送门

CF 传送门

我独立做出一道 *3000?

考虑对于单次询问,除了 \(O(nm)\) 的 dp,有没有什么方法能快速算出答案。发现若 \(a_{i + 1} - a_i < b_{j + 1} - b_j\)\(i \gets i + 1\),否则 \(j \gets j + 1\) 是最优的。这个贪心的证明不难,考虑当前新走到某一行或某一列的贡献是差分数组乘上剩下的格子个数,显然选较小的乘上去最优。

那么现在单次询问我们能做到 \(O(n + m)\) 了。观察到 \(n \le 100\),考虑从这里入手。可以枚举每一行,二分出在这一行能走到的最右的列(也就是最大的 \(j\) 使得 \(b_{j + 1} - b_j < a_{i + 1} - a_i\))。用一棵树状数组维护 \(b\) 的二阶差分即可树状数组上二分找到这个位置。

\(a\) 对答案的贡献每次枚举 \(i\) 直接计算即可,\(b\) 对答案的贡献不难发现是 \([1, m]\) 的和加上一些单点的形式(单点的贡献是向下走产生的)。查 \(b\) 的单点的值,考虑转化成 \(b\) 的一阶差分的前缀和,也可以用树状数组维护。

总时间复杂度为 \(O((nm + q) \log m + nq)\)

code
// Problem: G. A Stroll Around the Matrix
// Contest: Codeforces - Deltix Round, Autumn 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1609/G
// Memory Limit: 256 MB
// Time Limit: 6000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 100100;

ll n, m, q, a[maxn], b[maxn], d[maxn], sb;

inline ll calc(ll l, ll r) {
	return (l + r) * (r - l + 1) / 2;
}

struct {
	ll c[maxn];
	
	inline void update(ll x, ll d) {
		for (int i = x; i <= m; i += (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline ll query(int x) {
		ll res = 0;
		for (int i = x; i; i -= (i & (-i))) {
			res += c[i];
		}
		return res;
	}
	
	inline ll find(ll x) {
		ll s = 0, p = 0;
		for (int i = 17; ~i; --i) {
			if (p + (1 << i) < m && s + c[p + (1 << i)] < x) {
				p += (1 << i);
				s += c[p];
			}
		}
		return p;
	}
} t1;

struct {
	ll a[maxn], b[maxn];
	
	inline void update(int x, ll y) {
		for (int i = x; i <= m; i += (i & (-i))) {
			a[i] += y;
			b[i] += x * y;
		}
	}
	
	inline void update(int l, int r, ll x) {
		update(l, x);
		update(r + 1, -x);
	}
	
	inline ll query(int x) {
		ll res = 0;
		for (int i = x; i; i -= (i & (-i))) {
			res += a[i] * (x + 1) - b[i];
		}
		return res;
	}
} t2;

inline ll query(int x) {
	// printf("x: %d %lld\n", x, t2.query(x - 1) + b[1]);
	return t2.query(x - 1) + b[1];
}

inline ll work() {
	ll j = 1, ans = a[n];
	for (int i = 1; i < n; ++i) {
		ll p = max(j - 1, t1.find(a[i + 1] - a[i]));
		ans += a[i] * (p - j + 1);
		j = p + 1;
		ans += a[i] + query(j);
		// printf("%d %lld %lld\n", i, j, ans);
	}
	ans += a[n] * (m - j) + sb;
	return ans;
}

void solve() {
	scanf("%lld%lld%lld", &n, &m, &q);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	for (int i = 1; i <= m; ++i) {
		scanf("%lld", &b[i]);
		sb += b[i];
	}
	for (int i = 1; i < m; ++i) {
		d[i] = b[i + 1] - b[i];
		t1.update(i, d[i] - d[i - 1]);
		t2.update(i, i, d[i]);
	}
	while (q--) {
		ll op, x, y;
		scanf("%lld%lld%lld", &op, &x, &y);
		if (op == 1) {
			for (int i = n - x + 1; i <= n; ++i) {
				a[i] += (i - n + x) * y;
			}
		} else {
			sb += x * (x + 1) / 2 * y;
			x = m - x + 1;
			if (x == 1) {
				b[1] += y;
				x = 2;
			}
			t1.update(x - 1, y);
			t2.update(x - 1, m, y);
		}
		// printf("sb: %lld\n", sb);
		printf("%lld\n", work());
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-01-22 14:51  zltzlt  阅读(11)  评论(0)    收藏  举报