二分答案
二分一般直接二分答案mid,然后利用题目中的某个限制来检验这个mid是否是正确。
不正确就接着分。
一般框架:
int l, r; while (l <= r) { int mid = (l + r) / 2; if (check(mid)) 答案就是mid; else 根据题意,l = mid + 1 或者 r = mid - 1; }
代码:
#include <cstdio> const int MAXN = 5e4 + 7; int s, n, m, a[MAXN]; int check(int x) { int last = 0, re = 0; for (int i = 1; i <= n + 1; i++) if (a[i] - last < x) re++; else last = a[i]; return re; } int main() { scanf("%d%d%d", &s, &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); a[n + 1] = s; int l = 1, r = s, ans = 0; while (l <= r) { int mid = (l + r) >> 1; int f = check(mid); // printf("when mid = %d, f = %d ", mid, f); if (f <= m) l = mid + 1, ans = mid; else r = mid - 1; } printf("%d ", ans); return 0; }
对于10% 的数据,有 1 ≤n ,m≤10;
对于30% 的数据,有 1 ≤n ,m≤500 ;
对于50% 的数据,有 1 ≤n ,m≤5,000;
对于70% 的数据,有 1 ≤n ,m≤10,000 ;
对于100%的数据,有 1 ≤n ,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1 ≤Li ≤Ri ≤n 。
首先,单纯的二分。
#include <cstdio> #include <cstring> #include <cmath> #define ll long long const int MAXN = 200007; ll n, m, s, v[MAXN], w[MAXN], q[MAXN][2], rez = 1e13, retn; ll check(ll x) { ll ans = 0, f = 0, retn = 0; for (ll i = 1; i <= m; i++) { f = 0, ans = 0; for (ll j = q[i][0]; j <= q[i][1]; j++) if (w[j] >= x) ans += v[j], f++; retn += ans * f; } retn = s - retn; return retn; } int main() { scanf("%lld%lld%lld", &n, &m, &s); for (int i = 1; i <= n; i++) scanf("%lld%lld", &w[i], &v[i]); for (int i = 1; i <= m; i++) scanf("%lld%lld", &q[i][0], &q[i][1]); int l = 0, r = 1e6 + 1; while (l <= r) { ll mid = (l + r) / 2; ll p = check(mid); if (p > 0) { if (p < rez) rez = p; r = mid - 1; } else if (!p) { printf("0 "); return 0; } else { p = -p; if (p < rez) rez = p; l = mid + 1; } // printf("%lld ", p); } printf("%lld ", rez); return 0; }
check() 里是 n^2 的,肯定是A不了的,只过了50%的数据。
于是我们来想一想优化。
前缀和优化!