题解 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;
}

浙公网安备 33010602011771号