1 条题解
-
0
题意
给定大小 的矩阵 ,记 。
可以选择 个数 满足 且 。求 的最大值。
,。
对任意 有 或 ,即对任意 有 单调不减或单调不增。
解析
首先,我们可以将不增的和不减的分开考虑。
设 为只考虑不增的时, 时的最大值, 为只考虑不减的时, 时的最大值。
则答案 $\displaystyle ANS = \max_{0 \le x \le V} (f_x + g_{V - x})$。
先求 ,其中 。
设令 不增的 有 个,则 时有 。所以只需要求 时的 。
贪心。将所有这样的 对应的 个 放在一起排序,取最大的 个,它们的和就是 。
:::success[证明] 只需证明这样是合法的,即证:若取了某个 ,则一定也取了 。
因为 ,而又是从大到小取的,所以一定也取了 。证毕。 :::
再求 ,其中 。
同理,设令 不减的 有 个,则 时有 。所以只需要求 时的 。
对于这样的 ,注意到 ,所以 。
所以 为下凸函数!
而 为下凸函数的卷积,所以合理猜测当 几乎都取到边界时 最大。
猜测: 最大时,可以使 中至多有一个数 。
:::success[证明] 反证法。假设有至少两个 满足 。则这两行都选了但没选完。
设其中一行最后一个选的数为 ,第一个没选的数为 ,另一行最后一个选的数为 ,第一个没选的数为 。
则 ,。
若 ,则 。将 去掉并加入 ,则 不减,矛盾。
若 ,则 。将 去掉并加入 ,则 增加,亦矛盾。
证毕。 :::
所以 取到最大值时,只有至多一行选了但没选完,其余的要么全选了要么都没选。
于是可以通过带余除法确定选了但没选完的那一行选了几个,以及有多少行全选了。
设 ,其中 为非负整数,。
分以下两种情况:
-
若 ,则一定是有 行全选了,其余的都没选。于是取总和最大的 行即可。
-
若 ,则有 行全选了,有一行只选了 个,其余的行没选。
首先贪心的选取总和最大的 行为全选的,则取剩下的行中 最大的 对应的行选 个。
可以发现这样贪心是不对的,于是再考虑选 个的行在总和最大的 行中的情况。此时需要选剩下的当中总和最大的 行全选,即总和最大的 行除去选 个的行。
所以可以先选了总和最大的 行,再选其中的一行删掉后 个。取其中后 个的和最小的一行即可。
其中需要求总和最大的 行的和,不在总和最大的 行中的行的 的最大值,总和最大的 行中后 个的和的最小值,如果先按照总和从大到小的顺序将这 行排序,则依次为前缀和,后缀最大值,前缀最小值,可以预处理。
这样就可以求出 了。最后再求 即可。
时间复杂度 。
:::success[代码]
#define N 300005 #define ll long long int n, m, k; ll a[N]; struct node { vector<ll> sum; node () {} void init() {sum.resize(m + 1, 0);} friend bool operator < (node a, node b) {return a.sum[m] > b.sum[m];} }; node nd[N]; ll sum[N]; ll pre[N], suc[N]; int f(int i, int j) {return (i - 1) * m + j;} int cnt = 0; priority_queue<ll> q; ll ans[N], ans_[N]; int main() { n = read<int>(); m = read<int>(); k = read<int>(); for (int i = 1; i <= n; i++) { bool flg = true; for (int j = 1; j <= m; j++) { a[j] = read<ll>(); if (j > 1 && a[j] > a[j - 1]) flg = false; } if (flg) { for (int j = 1; j <= m; j++) q.push(a[j]); } else { nd[++cnt].init(); for (int j = 1; j <= m; j++) nd[cnt].sum[j] = nd[cnt].sum[j - 1] + a[j]; } } sort(nd + 1, nd + cnt + 1); ll sm = 0; for (int i = 1; i <= (n - cnt) * m; i++) { sm += q.top(); q.pop(); ans[i] = sm; } for (int i = (n - cnt) * m + 1; i <= n * m; i++) ans[i] = ans[i - 1]; for (int i = 1; i <= cnt; i++) sum[i] = sum[i - 1] + nd[i].sum[m]; for (int i = 1; i <= cnt; i++) { for (int j = 1; j <= m; j++) { if (i == 1) pre[f(i, j)] = nd[i].sum[j] - nd[i].sum[m]; else pre[f(i, j)] = max_(pre[f(i - 1, j)], nd[i].sum[j] - nd[i].sum[m]); } } for (int i = cnt; i >= 1; i--) { for (int j = 1; j <= m; j++) { if (i == cnt) suc[f(i, j)] = nd[i].sum[j]; else suc[f(i, j)] = max_(suc[f(i + 1, j)], nd[i].sum[j]); } } for (int i = 1; i <= cnt * m; i++) { if (i % m == 0) { ans_[i] = sum[i / m]; } else { int x = i / m, r = i % m; ans_[i] = max_(sum[x] + suc[f(x + 1, r)], sum[x + 1] + pre[f(x + 1, r)]); } } for (int i = cnt * m + 1; i <= n * m; i++) ans_[i] = ans_[i - 1]; ll ANS = 0; for (int i = 0; i <= k; i++) ANS = max_(ANS, ans[i] + ans_[k - i]); writeln(ANS); return fl(); }:::
THE END
-
- 1
信息
- ID
- 11496
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者