题解 P9457 [入门赛 #14] 魔法少女扶苏 (Hard Version)

Update on 2023/7/16 21:51 发现优先队列的英文写错了,已经更正。

分析

首先我们对于 $a_{x,y}$ 设需要至少进行 $c$ 次操作才能满足 $a_{x, y} \geq \sum \limits _{i = 1}^n a_{i,y} + \sum \limits _{i = 1}^m a_{x,i}$。

进行 $k$ 次操作后,需要满足:

$$ a_{x, y} - c \geq \sum \limits _{i = 1}^n a_{i,y} -nc + \sum \limits _{i = 1}^m a_{x,i} - mc $$

移一下项可以得到:

$$ (n + m - 1)c \ge \sum \limits _{i = 1}^n a_{i,y} + \sum \limits _{i = 1}^m a_{x,i} - a_{x,y} $$

因为 $n + m - 1 > 0$,所以又可以得到:

$$ c \ge \dfrac{\sum \limits _{i = 1}^n a_{i,y} + \sum \limits _{i = 1}^m a_{x,i} - a_{x,y}}{n+m-1} $$

所以:

$$ c = \left\lceil\dfrac{\sum \limits _{i = 1}^n a_{i,y} + \sum \limits _{i = 1}^m a_{x,i} - a_{x,y}}{n+m-1}\right\rceil $$

接着对每一个进行计算,取第 $k$ 小即可。

我用的是优先队列 std::priority_queue,时间复杂度 $O(nm \log k)$。

代码

//the code is from chenjh
#include<cstdio>
#include<queue>
int n,m,k;
long long a[3003][3003],s[3003],q[3003];//s[i] 表示第 i 行的元素之和,q[j] 表示第 j 列的元素之和。
std::priority_queue<long long> Q;//建立一个大根堆。
// #define DEBUG 1  // 调试开关
struct IO {//快读快输,这一部分可以跳过。
#define MAXSIZE (1 << 20)
#define isdigit(x) (x >= '0' && x <= '9')
  char buf[MAXSIZE], *p1, *p2;
  char pbuf[MAXSIZE], *pp;
#if DEBUG
#else
  IO() : p1(buf), p2(buf), pp(pbuf) {}

  ~IO() { fwrite(pbuf, 1, pp - pbuf, stdout); }
#endif
  char gc() {
#if DEBUG  // 调试,可显示字符
    return getchar();
#endif
    if (p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin);
    return p1 == p2 ? ' ' : *p1++;
  }

  bool blank(char ch) {
    return ch == ' ' || ch == '\n' || ch == '\r';
  }

  template <class T>
  void read(T &x) {
    x = 0;
    char ch = gc();
    for (; !isdigit(ch); ch = gc());
    for (; isdigit(ch); ch = gc()) x = x * 10 + (ch - '0');
  }

  void push(const char &c) {
#if DEBUG  // 调试,可显示字符
    putchar(c);
#else
    if (pp - pbuf == MAXSIZE) fwrite(pbuf, 1, MAXSIZE, stdout), pp = pbuf;
    *pp++ = c;
#endif
  }

  template <class T>
  void write(T x) {
    if (x < 0) x = -x, push('-');  // 负数输出
    static T sta[35];
    T top = 0;
    do {
      sta[top++] = x % 10, x /= 10;
    } while (x);
    while (top) push(sta[--top] + '0');
  }

} io;

int main(){
    io.read(n),io.read(m),io.read(k);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)io.read(a[i][j]),s[i]=s[i]+a[i][j],q[j]=q[j]+a[i][j];
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
        Q.push((s[i]+q[j]-a[i][j]+n+m-2)/(n+m-1));//因为要向上取整,所以要加上 n+m-2。
        while((int)Q.size()>k) Q.pop();超出 k,将堆顶弹出。
    }
    io.write(Q.top());//取出第 k 小。
    return 0;
}
posted @ 2023-07-16 21:45  Chen_Jinhui  阅读(82)  评论(0)    收藏  举报  来源