CodeForces 1814D Balancing Weapons

洛谷传送门

CF 传送门

2500

下文令题中的 \(k\)\(m\)

显然每个 \(d_i\) 都修改,次数就为 \(n\)

考虑枚举 \(i\),钦定 \(d_i\) 不修改,然后枚举 \([l, l + m - 1], l \in [f_i \times d_i - m, f_i \times d_i]\) 为最后所有 \(p_k\) 的取值范围(令 \(r = l + m - 1\)),那么 \([l, r]\) 合法当且仅当 \(\forall k \in [1, n], \exists x \in [l, r], a_k \mid x\),也就是 \(\forall k \in [1, n], \left\lfloor\frac{r}{a_k}\right\rfloor \ne \left\lfloor\frac{l-1}{a_k}\right\rfloor\)。在此基础上修改次数为 \(\sum\limits_{k=1}^n [f_k \times d_k \notin [l,r]]\)。暴力枚举,复杂度 \(O(n^2m)\)

考虑把判定是否合法和统计答案的 \(n\) 变成 \(\log\)。根据抽屉原理,若 \(a_k \le m\)\([l, l + m - 1]\) 中至少有一个数是 \(a_k\) 的倍数。若 \(a_k > m\)\([f_i \times d_i - m, f_i \times d_i + m]\)\(a_k\) 倍数的数是 \(O(1)\) 个。找到这些数,标记,这样判定就只用查前缀和了;修改次数为 \(\sum\limits_{k=1}^n [f_k \times d_k \notin [l,r]]\),很容易发现也可以离散化后前缀和优化。复杂度就变成 \(O(nm \log (n + m))\) 了。

code
// Problem: D. Balancing Weapons
// Contest: Codeforces - Educational Codeforces Round 146 (Rated for Div. 2)
// URL: https://codeforces.com/group/bXwyZVR0kC/contest/1814/problem/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

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

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

const int maxn = 3030;

ll n, m, a[maxn], b[maxn], lsh[maxn * 100], tot;

struct BIT {
	ll c[maxn * 100], n;
	
	inline void init(int _n) {
		n = _n;
		for (int i = 0; i <= n; ++i) {
			c[i] = 0;
		}
	}
	
	inline void update(ll x, ll d) {
		for (ll i = x; i <= n; i += (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline ll query(ll x) {
		ll res = 0;
		for (ll i = x; i; i -= (i & (-i))) {
			res += c[i];
		}
		return res;
	}
	
	inline ll query(int l, int r) {
		return query(r) - query(l - 1);
	}
} t1, t2;

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &b[i]);
	}
	ll ans = n;
	for (int i = 1; i <= n; ++i) {
		ll x = a[i] * b[i], cnt = 0;
		tot = 0;
		t2.init(m * 2 + 10);
		for (ll t = x - m; t <= x + m; ++t) {
			lsh[++tot] = t;
		}
		for (int k = 1; k <= n; ++k) {
			lsh[++tot] = a[k] * b[k];
			if (a[k] <= m) {
				continue;
			}
			++cnt;
			for (ll t = max(x - m, a[k]) / a[k] * a[k]; t <= x + m; t += a[k]) {
				if (x - m <= t && t <= x + m) {
					t2.update(t - (x - m) + 1, 1);
				}
			}
		}
		sort(lsh + 1, lsh + tot + 1);
		tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
		t1.init(tot);
		for (int k = 1; k <= n; ++k) {
			int id = lower_bound(lsh + 1, lsh + tot + 1, a[k] * b[k]) - lsh;
			t1.update(id, 1);
		}
		for (ll l = x - m; l <= x; ++l) {
			ll r = l + m;
			int L = lower_bound(lsh + 1, lsh + tot + 1, l) - lsh;
			int R = lower_bound(lsh + 1, lsh + tot + 1, r) - lsh;
			if (t2.query(l - (x - m) + 1, r - (x - m) + 1) == cnt) {
				ans = min(ans, n - t1.query(L, R));
			}
		}
	}
	printf("%lld\n", ans);
}

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

posted @ 2023-05-12 16:33  zltzlt  阅读(39)  评论(0)    收藏  举报