T1 [JZOJ6309] 完全背包
题目描述
数据范围
分析
首先显然可以把体积大价值小,或者相同体积中价值较小的物品舍弃
然后有这样一个定理,在 $n$ 个整数中一定存在若干个整数之和为 $n$ 的倍数
证明就是在所有前缀和(包括第 $0$ 项)中,必定存在两个前缀和模 $n$ 的余数相等,所以这两个数之间的区间和(前开后闭)是 $n$ 的倍数
设 $s$ 为性价比最高的物品中 $a_i$ 最小的物品,$x$ 为最优情况下非 $s$ 物品的种类
当 $x \geq a_s$ 时,可以将若干个 $a_i$ 之和为 $a_s$ 倍数的物品用 $s$ 替换,此时结果一定不会更劣
所以一定存在 $x < a_s$ 的最优方案,这 $x$ 个物品的 $a_i$ 之和一定不会超过 $100a_s$
这样我们就可以先取 $\lfloor \frac{m}{a_s} \rfloor - 100$ 个 $s$ 物品,再在剩下的空间里做多重背包
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <set> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 1000005 int n, tot, last; ll m, f[N]; struct Data { int a, b; } w[N], v[N]; bool cmp(Data x, Data y) { if (x.a != y.a) return x.a < y.a; return x.b > y.b; } int main() { scanf("%d%lld", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &w[i].a, &w[i].b); sort(w + 1, w + n + 1, cmp); for (int i = 1; i <= n; i++) if (w[i].b > last) last = w[i].b, v[++tot] = w[i]; Data best = v[1]; for (int i = 2; i <= tot; i++) if (best.a * v[i].b > v[i].a * best.b) best = v[i]; ll t = m / best.a - 100; ll ans = t * best.b; m -= t * best.a; for (int i = 1; i <= tot; i++) for (int j = v[i].a; j <= m; j++) f[j] = max(f[j], f[j - v[i].a] + v[i].b); printf("%lld\n", ans + f[m]); return 0; }
T2 [JZOJ6208] 中间值
题目描述
数据范围
分析
这题的原型是求两个有序数列中的第 $k$ 大/小数,通常这类题有两种 $O(log \; n)$ 的做法(以第 $k$ 小为例)
一种是在两个序列中分别取第 $\frac{k}{2}$ 小的数,此时取出的数较小的序列的前 $\frac{k}{2}$ 个数中一定不存在答案,所以就可以缩小区间查找第 $k-\frac{k}{2}$ 小的数,直到有一个序列的长度变为 $0$
另一种是在两个序列中分别取中位数 $a_i,b_j$,假设 $a_i \leq b_j$,若 $i+j \leq k$,则答案一定不在 $a$ 的前半段,否则答案一定不在 $b$ 的后半段,每次操作都将一个序列的长度减半
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <set> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 500005 int n, m, opt, ans; int a[N], b[N]; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') {if ( ch == '-') f = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();} return x *= f; } int kth(int al, int ar, int bl, int br, int k) { if (al > ar) return b[bl + k - 1]; if (bl > br) return a[al + k - 1]; if (k == 1) return min(a[al], b[bl]); int mid = k >> 1, an = inf, bn = inf; if (al + mid - 1 <= ar) an = a[al + mid - 1]; if (bl + mid - 1 <= br) bn = b[bl + mid - 1]; if (an <= bn) return kth(al + mid, ar, bl, br, k - mid); else return kth(al, ar, bl + mid, br, k - mid); } int main() { n = read(); m = read(); for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 1; i <= n; i++) b[i] = read(); while (m--) { opt = read(); if (opt == 1) { int x = read(), y = read(), z = read(); if (!x) a[y] = z; else b[y] = z; } else { int l1 = read(), r1 = read(), l2 = read(), r2 = read(); ans = kth(l1, r1, l2, r2, (r1 - l1 + r2 - l2 + 3) / 2); printf("%d\n", ans); } } return 0; }
T3 [JZOJ6306] Sequence
题目描述
数据范围
分析
暂且贴个solution