目录
第一题:区间三等分
数据范围很大,暴力显然超时,题目要求我们去求数组三等分的方法数,先进行前缀和处理,得到前缀和数组,问题就转化为,找到i, j其中i < j,3 * sum[i] = total_sum ,3 * sum[j] = total_sum * 2。这样只需要考察对于j前面有多少个i即可,虽然可以再次求前缀和不过没有必要。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e5 + 5;
long long sum[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
ll s = 0;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
s += a;
sum[i] = s;
}
int a = 0;
ll ans = 0;
for (int i = 1; i < n; i++) {
if (sum[i - 1] * 3 == sum[n - 1]) {
a++;
}
if (i < n - 1 && sum[i] * 3 == sum[n - 1] * 2) {
ans += a;
}
}
cout << ans << '\n';
return 0;
}
第二题:二维前缀和(最大子矩阵)
如果还要优化的话可以用二维的树状数组来解决。
第一次处理:求出从边界(上和左)到某个点的子矩阵的和。
第二次处理:对于每个上述的矩阵求出所有可能的分割后的矩阵,因为最大的矩阵不一定会接着边缘。
记从1行1列到i行j列的矩阵为sum[i][j]其实这里也算是某种动态规划。
0 | 0 | 0 |
0 | sum[i - 1][j - 1] | sum[i - 1][j] |
0 | sum[i][j - 1] | sum[i][j](未知) |
0 | 0 | 0 |
0 | sum[k][l] | sum[k][j] |
0 | sum[i][l] | sum[i][j] |
不难得到实现。
实现:
#include <bits/stdc++.h>
using namespace std;
const int N = 150;
int sum[N][N], a[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
}
}
int ans = -INT_MAX;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 0; k <= i; k++) {
for (int l = 0; l <= j; l++) {
ans = max(ans, sum[i][j] - sum[k][j] - sum[i][l] + sum[k][l]);
}
}
}
}
cout << ans << '\n';
return 0;
}
第三题:一维的前缀和(一道有趣的小题)
求出总时间,然后求出最大的差即可求出答案,根据单调性只需要比较两个点之间的差即可。
#include <bits/stdc++.h>
using namespace std;
long long sum[1000001];
int main() {
long long n, k, a, maxx = 0;
cin >> n >> k;
sum[1] = 0;
for (int i = 2; i <= n; i++) {
cin >> a;
sum[i] = sum[i - 1] + a;
}
for (int i = k + 1; i <= n; i++) maxx = max(maxx, sum[i] - sum[i - k]);
cout << sum[n] - maxx << '\n';
return 0;
}
第四题:前缀+二分(其实可以不用)
这道题我们要关注的是刚好是平方数的临界状态,越过这个临界状后改变量会变化,表达式会改变。这里的时间复杂度设计得比较宽松,即使不用二分也可以过。
实现:
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N = 3000;
ll d[N], sum[N]; //d表示的第一个变化量为i的点值
int bs(ll x) {
int l = 1, r = 2961, mid;
while (l < r) {
mid = l + r + 1 >> 1;//找到最后一个小于等于它的值
if (d[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
d[2] = 1;
for (ll i = 3; i <= 2961; i++) {
d[i] = i * i * 114 - 514;
}
sum[2] = 2;//前缀和,刚好到这里的和是多少,主意边界
for (ll i = 3; i <= 2961; i++) {
sum[i] = (d[i] - d[i - 1] - 1) * (i - 1) + sum[i - 1] + i;
}
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
ll n;
cin >> n;
int pos = bs(n);
cout << sum[pos] + pos * (n - d[pos]) << '\n';//加上多出来的部分
}
return 0;
}