题意

给你一个n * m的矩阵,让你做K次操作,使得最后得到的值最大。

操作有两种:

一是在任意一行上操作,最终的结果值加上这一行数的和,然后这一行每一个数都要减去p。

二是在任意一列上操作,最终的结果值加上这一列数的和,然后这一列每一个数都要减去p。

数据范围:1 ≤ n, m ≤ 10^3; 1 ≤ k ≤ 10^6; 1 ≤ p ≤ 100

退化版的题目思路

如果我们只限定一种操作,此题就是简单题了。我们维护一个大根堆,堆中保存每一行(或列)之和。

每次操作只需要取出最大值,加到最终结果上。之后将这个值减去对应的p * n(或p * m),再加入堆中。

经过K次循环,就可以得到最后的答案了。

真正的题目思路

对于行和列同时操作,我们也可以套用这种方法。但是要解决对行操作后,对列上数字的值的影响。

易得,如果行列混合操作,且只使用上述方法的话。每次行操作所得到的值,实际上为简单行操作得到的值 - 此次行操作之前的所有列操作次数 * p(为什么?)。对于列操作亦然。

假设我们有一个操作序列,<a0, b0>代表序列的一段执行了a0个行操作和b0个列操作。如下图所示:

<a0, b0>  | <1, 0>  | <0, 1>  | <a1, b1>|
|<---A--->|<---B--->|<---C--->|<---D--->|

操作被分为了四个阶段。

对于B阶段来说,其结果为1次简单行操作的值 - b0 * p(为什么?),对于C阶段来说,其结果为1次简单列操作的值 - (a0 + 1) * p

那么,如果当我们把B阶段和C阶段互换的话, 结果是不变的。(为什么?我真TMD是十万个为什么。。。)

所以我们可以得出,在任一个操作序列中,我们可以交换任意一个的行操作和列操作,而总结果不变。

最终,我们可以将操作序列等价变化成下面的序列。

<a, 0> | <0, b>

此时,最终的结果为a次简单行操作的值 + b次简单列操作的值 - a * b * p。(为什么?)

而a与b的值可以简单的通过枚举得到。思路有了,代码如果不出大问题,这题就可以AC了。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

#define print(x) cout << x << endl
#define input(x) cin >> x

typedef long long llint;

const int SIZE = 1024;
const int K = 1024000;

int n, m, k, p;
llint sumX[K + 5], sumY[K + 5];
vector<llint> xs, ys;

void do_calc(const vector<llint>& vs, llint sum[], int u)
{
    priority_queue<llint> pq;
    for (auto i: vs) {
        pq.push(i);
    }
    sum[0] = 0;
    for (int i = 1; i <= k; i++) {
        llint now = pq.top();
        pq.pop();
        sum[i] = sum[i - 1] + now;
        now -= (llint)p * u;
        pq.push(now);
    }
}

void calcX()
{
    do_calc(xs, sumX, m);
}

void calcY()
{
    do_calc(ys, sumY, n);
}

int main()
{
    int a;
    freopen("input.txt", "r", stdin);
    input(n >> m >> k >> p);
    ys.resize(m);
    xs.resize(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &a);
            ys[j] += a;
            xs[i] += a;
        }
    }

    calcX();
    calcY();

    llint ans = std::numeric_limits<llint>::min();
    for (int i = 0; i <= k; i++) {
        llint t = 0;
        t += sumX[i];
        t += sumY[k - i];
        t -= (llint)i * (k - i) * p;
        ans = max(ans, t);
    }
    print(ans);
    return 0;
}

后记

某厂的g++版本是3.4.x,没有C++11,根本秀不起来啊

说的就和你会写C++11一样_(:з」∠)_


Comments

comments powered by Disqus