天天看点

行列快乐值

给定一个n * mn∗m的矩阵,将对矩阵进行k次操作。每次可以进行如下某一种操作:

1.将某行的元素相加作为快乐值,然后将该行元素都减去p。

2.将某列的元素相加作为快乐值,然后将该列元素都减去p。

问k次之后快乐值总和的最大值是多少?

输入

第一行输入四个整数n, m, k, p (1 \le n, m \le 10^3, 1 \le k \le 10^6, 1 \le p \le 10^2)n,m,k,p(1≤n,m≤103,1≤k≤106,1≤p≤102)。

接下来n行,每行m个整数表示矩阵。

矩阵中的元素属于区间[1, 10^3][1,103]。

输出

输出最大快乐值总和。

样例

输入

复制

2 2 2 2
1 3
2 4      

输出

复制

11      

提示

先对第二列操作,然后对第二行操作

最后矩阵变成

1 1

0 0

子任务1,20分,2 \le n + m \le 10, 1 \le k \le 32≤n+m≤10,1≤k≤3。

子任务2, 80分,1 \le n, m \le 10^3, 1 \le k \le 10^61≤n,m≤103,1≤k≤106。

先找行还是先找列都是一样的,因为对行操作后,对列的和产生的影响是一样,反之也是如此,枚举所有的可能,找所有和的和最大的。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <algorithm>
#include <functional>
#define inf 0x3f3f3f3f
int main()
{
    int n, m, d, k, p;
    scanf("%d %d %d %d", &n, &m, &k, &p);
    std::vector<int> rsum(n, 0);
    std::vector<int> csum(m, 0);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            scanf("%d", &d);
            rsum[i] += d;
            csum[j] += d;
        }
    }
    std::vector<long long> sum(k + 1, 0);
    std::priority_queue<long long, std::vector<long long>, std::less<long long>> qr, qc;
    for (int i = 0; i < n; i++)
    {
        qr.push(rsum[i]);
    }
    for (int i = 0; i < m; i++)
    {
        qc.push(csum[i]);
    }
    long long ssumr = 0, ssumc = 0, temp;
    for (int i = 1; i <= k; i++)
    {
        temp = qr.top();
        qr.pop();
        qr.push(temp - m * p);
        ssumr += temp;
        sum[i] += ssumr;

        temp = qc.top();
        qc.pop();
        qc.push(temp - n * p);
        ssumc += temp;
        sum[k - i] += ssumc;

        if (i * 2 <= k)
        {
            temp = (long long)i * (k - i) * p;
            sum[i] -= temp;
            if (i != k - i) sum[k - i] -= temp;
        }
    }
    long long ans = sum[0];
    for (int i = 1; i <= k; i++)
    {
        if (sum[i] > ans)
        {
            ans = sum[i];
        }
    }
    printf("%lld", ans);
    return 0;
}