Codeforces Round #FF (Div. 2) D. DZY Loves Modification

来源:互联网 发布:房卡游戏源码 编辑:程序博客网 时间:2024/05/17 04:06

题目链接

http://codeforces.com/contest/447/problem/D

题目大意

给你一个矩阵, 设答案为ans = 0, 让你进行k次操作, 每次操作为选取一行或一列, ans+=该行或列所有元素之和, 加完后再把该行或列所有元素减去p
问你ans最大为多少

思路

首先这题的难点就是取行的时候会影响列, 取列的时候会影响行
那我们干脆先别考虑这些, 用r[i] 表示取i次行能得到的最大值
c[i]表示取i次列能得到的最大值
那么ans = max(ans, c[i] + r[k - i] - i * (k - i) * p)
我们来理解一下为什么要减去i * (k - i) * p
取一次行的话, 每取一次列都多加了一个p
以此类推

实现

我们可以先记录每一行的值的和cr[i], 以及每一列值的和cc[i], 再计算c[i], 以及r[i]的时候用两个优先队列来保证每次拿出的都是最大值

代码

#include<bits/stdc++.h>using namespace std;typedef long long ll;const ll INF = 0x3f3f3f3f3f3f3f3f;ll r[1000005], c[1000005], cr[1005], cc[1005];priority_queue<ll>qc, qr;int main(){    ll n, m, k, p;    scanf("%I64d%I64d%I64d%I64d", &n, &m, &k, &p);    for(int i=0; i<n; ++i)    {        ll tmp = 0, sr = 0, sc = 0;        for(int j=0; j<m; ++j)        {            scanf("%I64d", &tmp);            cc[j] += tmp;            cr[i] += tmp;        }    }    for(int i=0; i<m; ++i)        qc.push(cc[i]);    for(int i=0; i<n; ++i)        qr.push(cr[i]);    c[0] = 0, r[0] = 0;    for(int i=1; i<=k; ++i)    {        int tmp = qc.top();        qc.pop();        c[i] = c[i-1] + tmp;        qc.push(tmp - n*p);    }    for(int i=1; i<=k; ++i)    {        int tmp = qr.top();        qr.pop();        r[i] = r[i-1] + tmp;        qr.push(tmp - m*p);    }    ll ans = -INF;    for(ll i=0; i<=k; ++i)        ans = max(ans, r[i] + c[k-i] - (k-i)*i*p);    printf("%I64d\n", ans);    return 0;}

反思

  • 半个多月前这题毫无思路, 现在完全能独立思考出思路并且实现了, 果然还是变强了哇哈哈
  • 在思考如何保证每次选择都是最大值的时候卡了一下, 一下子没想到有优先队列这个东西, 一直在想别的办法更新,实在是太蠢了
  • 这题出现了两个bug, 一个是好像爆了int, 第二个把ans初始值赋为0去更新, 答案可能会小于0没考虑到