Social Infrastructure Information Systems Division, Hitachi Programming Contest 2020 D题题解

将题意转换为一开始\(t = 0\),第\(i\)个操作是令\(t \leftarrow (a_i + 1) t + (a_i + b_i + 1)\)。记\(A_i = a_i + 1, B_i = a_i + b_i + 1\)。问经过最多经过多少次操作后才能使得进行完这些操作后\(t \leq T\)仍然满足。

我们先推一个贪心性质:

若先进行\(i\)操作,再进行\(j\)操作时满足条件,且\(\frac{A_i - 1}{B_i} < \frac{A_j - 1}{B_j}\),则可以交换\(i\)\(j\)操作的顺序,使得条件仍然满足。

证明:由单调性,只需证明先进行\(i\)操作再进行\(j\)操作后的\(t\)的值大于先进行\(j\)操作再进行\(i\)操作后\(t\)的值。假设一开始的数值为\(t\),则第一种操作组合后,数值为\(A_iA_jt + A_jB_i + B_j\),第二种操作组合后数值为\(A_iA_jt + A_iB_j + B_i\)

由于\(A_jB_i + B_j > A_iB_j + B_i\)等价于\(\frac{A_i - 1}{B_i} < \frac{A_j - 1}{B_j}\),故结论成立!

因此我们可以将这些操作按\(\frac{A_i - 1}{B_i}\)从大到小排序,然后设计一个DP。设\(f_{i, j}\)表示进行了前\(i\)种操作的\(j\)次后\(t\)的值最小是多少。我们可以得到一个\(O(n^2)\)做法。注意到我们必定先进行\(A_i > 0\)的操作,而这样的操作最多进行\(O(\log T)\)次。再进行\(A_i = 0\)的操作时,必定是按照\(B_i\)从小到大顺序进行。所以我们只需把\(A_i > 0\)的操作拿来\(DP\),并且第二维只考虑到\(O(\log T)\)即可获得一个\(O(n (\log n + \log T))\)时间的算法。

代码如下:

#include <bits/stdc++.h>
using namespace std;

const int N = 200005, M = 40;

template <class T>
void read (T &x) {
	int sgn = 1;
	char ch;
	x = 0;
	for (ch = getchar(); (ch < '0' || ch > '9') && ch != '-'; ch = getchar()) ;
	if (ch == '-') ch = getchar(), sgn = -1;
	for (; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	x *= sgn;
}
template <class T>
void write (T x) {
	if (x < 0) putchar('-'), write(-x);
	else if (x < 10) putchar(x + '0');
	else write(x / 10), putchar(x % 10 + '0');
}

int n, cnt1 = 0, cnt2 = 0;
long long a[N], b[N], f[N][M + 1], t;

struct node {
	long long A;
	long long B;
	bool operator < (node rhs) const {
		long long val1 = (A - 1) * rhs.B;
		long long val2 = (rhs.A - 1) * B;
		return (val1 > val2) || (val1 == val2 && A < rhs.A);
	}
} vec[N];

long long num[N], pre[N];

int main () {
	read(n), read(t);
	for (int i = 1; i <= n; i++) {
		read(a[i]), read(b[i]);
		if (a[i]) {
			node x = {a[i] + 1, a[i] + b[i] + 1};
			vec[++cnt1] = x;
		}
		else num[++cnt2] = b[i] + 1;
	}

	sort(vec + 1, vec + cnt1 + 1);
	sort(num + 1, num + cnt2 + 1);

	pre[0] = 0ll;
	for (int i = 1; i <= cnt2; i++) pre[i] = pre[i - 1] + num[i];

	for (int i = 0; i <= M; i++) f[0][i] = t + 1;
	f[0][0] = 0ll;
	
	for (int i = 1; i <= cnt1; i++) {
		for (int j = 0; j <= M; j++) {
			f[i][j] = f[i - 1][j];
			if (j) f[i][j] = min(f[i][j], vec[i].A * f[i - 1][j - 1] + vec[i].B);
		}
	}

	int ans = 0;
	for (int i = 0; i <= M; i++) {
		if (f[cnt1][i] > t) continue;
		int pos = lower_bound(pre, pre + cnt2 + 1, t + 1 - f[cnt1][i]) - pre - 1;
		ans = max(ans, pos + i);
	}
	write(ans), putchar('\n');
	return 0;
}
posted @ 2020-03-29 21:03  unzcjouhi  阅读(189)  评论(0编辑  收藏  举报