选数字 (select
Time Limit:3000ms Memory Limit:64MB
题目描述
LYK 找到了一个 n*m 的矩阵,这个矩阵上都填有一些数字,对于第 i 行第 j 列的位置上
的数为 ai,j。
由于它 AK 了 noip2016 的初赛,最近显得非常无聊,便想到了一个方法自娱自乐一番。
它想到的游戏是这样的: 每次选择一行或者一列, 它得到的快乐值将会是这一行或者一列的
数字之和。之后它将该行或者该列上的数字都减去 p(之后可能变成负数) 。如此,重复 k
次,它得到的快乐值之和将会是它 NOIP2016 复赛比赛时的 RP 值。
LYK 当然想让它的 RP 值尽可能高,于是它来求助于你。
输入格式(select.in)
第一行 4 个数 n,m,k,p。
接下来 n 行 m 列,表示 ai,j。
输出格式(select.out)
输出一行表示最大 RP 值。
输入样例
2 2 5 2
1 3
2 4
输出样例
11
数据范围
总共 10 组数据。
对于第 1,2 组数据 n,m,k<=5。
对于第 3 组数据 k=1。
对于第 4 组数据 p=0。
对于第 5,6 组数据 n=1,m,k<=1000。
对于第 7,8 组数据 n=1,m<=1000,k<=1000000。
对于所有数据 1<=n,m<=1000,k<=1000000,1<=ai,j<=1000,0<=p<=100。
样例解释
第一次选择第二列,第二次选择第二行,第三次选择第一行,第四次选择第二行,第五
次选择第一行,快乐值为 7+4+2+0+-2=11。
/*
贪心+预处理.
处理出行和列分别选i次的最优值.
然后行选i次,列选k-i次相加减去中间重复的.
一开始暴力n==1处理和这个差不多.
暴力的时候也想处理出一些东西.
但是想到可能会对后面有影响
so 没处理(orz).
然后T挺了.
这题就是这点儿想不过来.
以后认识问题要深刻.
*/
#include<iostream>
#include<cstdio>
#include<queue>
#define LL long long
#define MAXN 1000001
#define MAXM 1001
using namespace std;
LL sum[MAXN],sum1[MAXN],ans=-1e18,c[MAXM],l[MAXM],xx;
LL n,m,k,p,g[MAXM][MAXM];
priority_queue<long long,vector<long long> >q;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*f;
}
void slove()
{
for(int i=1;i<=m;i++) q.push(c[i]);
for(int i=1;i<=k;i++) xx=q.top(),sum1[i]=sum1[i-1]+xx,q.pop(),q.push(xx-n*p);
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) q.push(l[i]);
for(int i=1;i<=k;i++) xx=q.top(),sum[i]=sum[i-1]+xx,q.pop(),q.push(xx-m*p);
for(int i=0;i<=k;i++)
ans=max(ans,sum[i]+sum1[k-i]-i*(k-i)*p);
return ;
}
int main()
{
freopen("select.in","r",stdin);
freopen("select.out","w",stdout);
n=read(),m=read(),k=read(),p=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
g[i][j]=read(),c[j]+=g[i][j],l[i]+=g[i][j];
slove();
cout<<ans;
return 0;
}