Record

[USACO20JAN] Time is Mooney G

简单动态规划。

由于 \(T\) 天会花费 \(C \times T^2\),所以每多一天的花费为 \(C \times (i^2 - (i-1)^2) = C \times (2i - 1)\)。因此,当 \(C \times (2i - 1) \ge max\{m_i\}\) 时就说明再怎么走也无法更优,于是结束程序。

code
#include <bits/stdc++.h>

using namespace std;

const int N = 1e3 + 5;

int n, m, ans, f[N][2], a[N], c, ma;
vector<int> e[N];

int main() {
	scanf("%d %d %d", &n, &m, &c);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), ma = max(ma, a[i]);
	for (int i = 1, u, v; i <= m; i++) {
		scanf("%d %d", &u, &v);
		e[v].push_back(u); 
	}
	memset(f, -0x3f, sizeof(f));
	f[1][0] = 0;
	for (int T = 1; c * (2 * T - 1) <= ma; T++) {
		for (int i = 1; i <= n; i++) {
			f[i][T & 1] = -0x3f3f3f3f;
			for (auto j : e[i])
				if (f[j][T - 1 & 1] != -0x3f3f3f3f)
					f[i][T & 1] = max(f[i][T & 1], f[j][T - 1 & 1] + a[i]);
		}
		ans = max(ans, f[1][T & 1] - c * T * T);
	}
	printf("%d\n", ans);
	return 0;
} 

[USACO20JAN] Farmer John Solves 3SUM G

发现不能直接通过一个 dp 求出 \(l \sim r\) 的答案。

但我们可以先固定三元组的第一个元素为左端点 \(i\),再枚举右端点 \(j\),计算得到包含 \(i\)\(j\) 且以它们为左右端点的三元组数量。

现在,就可以通过区间dp求出答案:\(ans_{l, r} = cnt_{l, r} + ans_{l + 1, r} + ans_{l, r - 1} - ans_{l + 1, r - 1}\)。因为 \(ans_{l + 1, r} + ans_{l, r - 1} - ans_{l + 1, r - 1}\) 不包含同时以 \(l\)\(r\) 为端点的情况,所以要加上 \(cnt_{l, r}\)

code
#include <bits/stdc++.h>

using namespace std;

const int N = 5e3 + 5, M = 3e6 + 5, NUM = 1e6;

int n, q, cnt[N][N], a[N], s[M];
long long f[N][N];

int main() {
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), a[i] += NUM;
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			if (j >= i + 2 && 3 * NUM >= a[i] + a[j] && NUM <= a[i] + a[j])
				cnt[i][j] = s[3 * NUM - a[i] - a[j]];
			s[a[j]]++;
		}
		for (int j = i + 1; j <= n; j++)
			s[a[j]]--;
	}
	for (int l = 2; l <= n; l++)
		for (int i = 1; i + l - 1 <= n; i++) {
			int j = i + l - 1;
			f[i][j] = f[i + 1][j] + f[i][j - 1] - f[i + 1][j - 1] + cnt[i][j];
		}
	while (q--) {
		int l, r;
		scanf("%d %d", &l, &r);
		printf("%lld\n", f[l][r]);
	}
	return 0;
} 

[USACO20JAN] Springboards G

发现答案等于 \(2N\) 减去跳过的距离,所以求最短走动距离只用求最多能跳过多少距离。dp 即可。

把每一组点拆开,然后从小到大排序,然后倒序枚举。

  • 当枚举到的点是经过传送到达的点时,记录 \(y\) 坐标不比它小的点中最多能跳过多少距离。

  • 否则,把它传送到的点所记录的答案加上这次传送所到达的距离与 \(y\) 坐标不比它小的点中最多能跳过多少距离比大小,取较大值。

至于如何快速求 \(y\) 坐标不比它小的点中最多能跳过多少距离,树状数组即可。

code
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;

int n, m, bit[N], ans, b[N], val[N];
struct node {
	int x, y, id, quantity;
	bool flag;
	inline node() {}
	inline bool operator<(node X) const { return x != X.x ? x < X.x : y < X.y; }
} a[N];

inline int lowbit(int x) { return x & (-x); }

inline void update(int x, int y) {
	while (x <= m * 2)
		bit[x] = max(bit[x], y), x += lowbit(x);
} 

inline int query(int x) {
	int ans = 0;
	while (x)
		ans = max(ans, bit[x]), x -= lowbit(x);
	return ans;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d %d", &a[i * 2 - 1].x, &a[i * 2 - 1].y, &a[i * 2].x, &a[i * 2].y);
		a[i * 2].flag = true, b[i * 2 - 1] = a[i * 2 - 1].y, b[i * 2] = a[i * 2].y;
		a[i * 2 - 1].id = a[i * 2].id = i;
		a[i * 2 - 1].quantity = a[i * 2].quantity = a[i * 2].x - a[i * 2 - 1].x + a[i * 2].y - a[i * 2 - 1].y;
	}
	sort(b + 1, b + m * 2 + 1), sort(a + 1, a + m * 2 + 1);
	for (int i = m * 2; i; i--) {
		a[i].y = 2 * m - (lower_bound(b + 1, b + m * 2 + 1, a[i].y) - b) + 1;
		if (a[i].flag)
			val[a[i].id] = query(a[i].y);
		else {
			val[a[i].id] = max(val[a[i].id] + a[i].quantity, query(a[i].y));
			update(a[i].y, val[a[i].id]);
			ans = max(ans, val[a[i].id]);
		}
	}
	printf("%d\n", n * 2 - ans);
	return 0;
}

[USACO20FEB] Timeline G

直接查分约束即可。

code
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

int n, m, c, dis[N];
bool vis[N];
deque<int> q;
vector<pair<int, int>> e[N];

inline void spfa(int s) {
	q.push_back(s), vis[s] = true;
	fill(dis + 1, dis + n + 1, -0x3f3f3f3f);
	while (!q.empty()) {
		int u = q.front();
		q.pop_front(), vis[u] = false;
		for (auto i : e[u]) {
			int v = i.first, w = i.second;
			if (dis[v] < dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!vis[v]) {
					if (q.empty() || dis[v] >= dis[q.front()])
						q.push_front(v);
					else
						q.push_back(v);
					vis[v] = true;
				}
			}
		}
	}
}

int main() {
	scanf("%d %d %d", &n, &m, &c);
	for (int i = 1, start; i <= n; i++) {
		scanf("%d", &start);
		e[0].emplace_back(i, start);
		e[i].emplace_back(0, -m);	
	}
	for (int i = 1, u, v, w; i <= c; i++) {
		scanf("%d %d %d", &u, &v, &w);
		e[u].emplace_back(v, w);
	}
	spfa(0);
	for (int i = 1; i <= n; i++)
		printf("%d\n", dis[i]);
	return 0;
}

[USACO20FEB] Help Yourself G

感觉求并不是很容易,于是想怎么可以求交来解决。

于是发现,当有 \(x\) 个连通块时,一定有 \(x + 1\) 个连续区间满足每一个区间都未被覆盖。

所以将 \(l \sim r\) 的区间处理为 \(0 \sim l - 1\)\(r + 1 \sim 2n + 1\) 即可。

注意:由于此题并不是在离散点集上处理,所以当有一个区间的 \(r\) 大于等于另一区间的 \(l\) 时才满足条件。

code
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 1e5 + 5, Mod = 1e9 + 7;

int n, m, ans, Pow[N], bit[N << 1];
pair<int, int> a[N << 1];

inline int lowbit(int x) { return x & (-x); }

inline void update(int x, int y) {
	x++;
	while (x <= 2 * n + 2)
		bit[x] += y, x += lowbit(x);
}

inline int query(int x) {
	x++;
	int sum = 0;
	while (x)
		sum += bit[x], x -= lowbit(x);
	return sum;
}

signed main() {
	Pow[0] = 1;
	for (int i = 1; i < N; i++)
		Pow[i] = Pow[i - 1] * 2 % Mod;
	scanf("%lld", &n);
	for (int i = 1, l, r; i <= n; i++) {
		scanf("%lld %lld", &l, &r), l++;
		a[++m] = make_pair(0, l - 1), a[++m] = make_pair(r + 1, 2 * n + 1);
	}
	sort(a + 1, a + m + 1);
	for (int i = 1; i <= m; ) {
		int j = i;
		for ( ; j <= m; j++)
			if (a[i].first == a[j].first);
			else
				break;
		(ans += Pow[i - query(a[i].first - 1) - 1] * (Pow[j - i] - 1) % Mod) %= Mod;
		j = i;
		for ( ; j <= m; j++) {
			if (a[i].first == a[j].first)
				update(a[j].second, 1);
			else
				break;
		}
		i = j;
	}
	printf("%lld\n", (ans - Pow[n] + 1 + Mod) % Mod);
	return 0;
}
posted @ 2024-10-13 16:26  zhy。  阅读(8)  评论(0编辑  收藏  举报