专栏文章
题解:P10282 [USACO24OPEN] Smaller Averages G
P10282题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mipjtfpu
- 此快照首次捕获于
- 2025/12/03 13:10 3 个月前
- 此快照最后确认于
- 2025/12/03 13:10 3 个月前
去年 USACO 的题目,比赛时没做出来,今天上课老师正好讲到了,才补出来。如此补题,怎能不爱?
先看数据,对于 很显然,直接暴力就行了。对于做题没什么启发。
然后是 的数据,这组数据是做题的出发点。我们考虑使用 DP,先解释 的含义。 代表 和 中划分相同份数且合法的方案数量。不难得出,在初始时,,而最后的答案位是 。
此时有一个 的做法。考虑枚举两个数组的上一个结束的位置 。不难得出方程,在满足
的时候, 。此时的条件含义十分明显,当 中在区间 中的元素的平均值不大于 中在区间 中的元素的平均值时,进行转移。
但是此时做法的复杂度完全无法接受, 时, 不会超时,但是对于 的数据,,可以接受的最大限度为 ,这里提一嘴,有几篇题解说最大限度是 是错误的,因为那个复杂度给到的是 的数据,即 时的数据。
所以现在重点在于考虑如何优化。
我们可以尝试优化 啦。但是观察后发现平均值无单调性,所以这个时候有一个领先人类智商巅峰一万年的方法。因为 本身范围其实不大,所以我们预处理出所有以 结尾的区间的平均值。
如果细品一下其实感觉挺有道理的,用 的时间换掉了 中 的一个 ,将时间优化为 。
对 把以 结尾的区间 按平均值排序,将得到的数组记作 ;同时对 把以 结尾的区间 按平均值排序,将得到的数组记作 。这时做法也就比较显然了,用双指针维护即可啦。
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 505, mod = 1e9 + 7;
int n, a[maxn], b[maxn];
struct node {
int id;
long double avg;
friend bool operator < (node x, node y) {
if (x.avg != y.avg) return x.avg < y.avg;
return x.id < y.id;
}
} s1[maxn][maxn], s2[maxn][maxn];
int s[maxn][maxn], f[maxn][maxn];
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] += a[i - 1];
for (int j = 0; j < i; j++) {
s1[i][j + 1] = (node){j, double(a[i] - a[j]) / (i - j)};
}
sort(s1[i] + 1, s1[i] + i + 1);
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
b[i] += b[i - 1];
for (int j = 0; j < i; j++) {
s2[i][j + 1] = (node){j, double(b[i] - b[j]) / (i - j)};
}
sort(s2[i] + 1, s2[i] + i + 1);
}
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= n; j++) {
for (int k = 1; k <= i; k++) {
s[j][k] = s[j][k - 1] + f[s1[i][k].id][j];
s[j][k] %= mod;
}
}
for (int j = 1; j <= n; j++) {
for (int k = 1, l = 1; l <= j; l++) {
while (k <= i && s1[i][k].avg <= s2[j][l].avg) k++;
f[i][j] += s[s2[j][l].id][k - 1];
f[i][j] %= mod;
}
}
}
cout << f[n][n] << '\n';
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...