CF446B DZY Loves Modification 【思维/优先队列】By cellur925

题目传送门

题目大意:给一个 \(n*m\) 的矩阵,并进行 \(k\) 次操作,每次操作将矩阵的一行或一列的所有元素的值减 \(p\) ,得到的分数为这次修改之前这一列/一行的元素和,求分数最大值。

我开始的意识流想法是用一个优先队列维护,先把所有元素插入,然后\(k\)次每次取出堆顶,减去乘\(p\)的什么东西,再塞回队中。但是...行与列是会互相影响的鸭,那么怎么搞呢?然后就不会了hh。

正解是努力打破了行与列之间的相互影响,把行与列分开计算。我们考虑行与列是怎么互相影响的:选择\(i\)个行,\(j\)个列,那么这里\(j=k-i\)。显然会产生\(i\)*\((k-i)\)个交点,我们需要另减去这些交点的贡献。

我们分别处理出行&列后,枚举最终的\(i\)就好了:)


#include<cstdio>
#include<algorithm>
#include<queue>

using namespace std;
typedef long long ll;

int n,m,k,p;
int f[2018][2018];
ll hang[2000],lie[2000],hang_cnt[1000900],lie_cnt[1000900];
ll ans=-1e18;
priority_queue<ll>q;

int main()
{
    scanf("%d%d%d%d",&n,&m,&k,&p);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&f[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            lie[j]+=f[i][j],hang[i]+=f[i][j];
    for(int i=1;i<=m;i++) q.push(lie[i]);
    for(int i=1;i<=k;i++)
    {
        ll tmp=q.top();q.pop();
        lie_cnt[i]=lie_cnt[i-1]+tmp;
        tmp-=1ll*n*p;
        q.push(tmp);
    }
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++) q.push(hang[i]);
    for(int i=1;i<=k;i++)
    {
        ll tmp=q.top();q.pop();
        hang_cnt[i]=hang_cnt[i-1]+tmp;
        tmp-=1ll*m*p;
        q.push(tmp);
    }
    for(int i=0;i<=k;i++)
        ans=max(ans,hang_cnt[i]+lie_cnt[k-i]-1ll*i*(k-i)*p);
    printf("%lld\n",ans);
    return 0;
}

注意开始数组开小了\(k\)\(1e5\)级别的。到现在还犯这种智障错误

posted @ 2018-11-01 22:49  cellur925&Chemist  阅读(360)  评论(0编辑  收藏  举报