题意
给你一个长为R宽为C的矩阵,第i行j列的数为Pi,j.
有m次询问,每次有5个参数xl,xr,yl,yr,h. 求以(xl,yl)为左上角和(xr,yr)为右下角的矩形中,至少要选几个值,使得它们的和≥h.
数据范围
对于50%的数据,满足R,C≤200,M≤200,000
另有50%的数据,满足R=1,C≤500,000,M≤20,000
对于100%的数据,满足1≤Pi,j≤1,000,1≤h≤2,000,000,000
题解
这道题很有意思2333
这个题相当于二合一吧,两种不同的方法解决它的子问题(但殊途同归).
-
第一个R,C≤200,M≤200,000.
这个是有点类似于暴力的做法,首先预处理出每种数字出现次数的关于位置和数字大小前缀和,以及出现数字和关于位置数字大小的前缀和.
这个有点绕,直接举例子吧.比如我程序中的Sum[i][j][k]
和Tot[i][j][k]
.
Sum[i][j][k]
就是以(1,1)为左上角(i,j)为右下角矩形的,数字≥k的和.
Tot[i][j][k]
就是以(1,1)为左上角(i,j)为右下角矩形的,数字≥k的出现次数的和.
然后每次就可以二分你需要选的最小的数字了,每次判断可行就直接前缀和容斥就行了.
然后这个选的最小数字不一定要选满,要最后算一下这个数字要选多少个.
令n=max(Pi,j)时间复杂度O(nRC+mlogn),空间复杂度O(nRC).
-
第二个R=1,C≤500,000,M≤20,000.
这个就是一个序列操作了,这个我认为是这道题的精髓.
我们沿用前一个算法的思想,也是要处理序列上那两个东西.
但时间复杂度肯定要进行优化. 所以有一个数据结构可以支持这个操作,就是主席树!
主席树不仅支持查找区间第k大,而且还能支持查找区间在[l,r]中数字的和 和 出现数字的和!(看来我数据结构学的真的蠢啊) 以后出现这种题要往这上面想想了.
然后我们可以每次算答案的时候直接在主席树上二分就行了和刚才的操作差不多吧,也类似于查找第k大.
时间复杂度O(Clogn+mlogn),空间复杂度O(Clogn).
代码
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("P1926.in", "r", stdin);
freopen ("P1926.out", "w", stdout);
#endif
}
const int N = 5e5 + 1e3;
const int maxnode = N * 20;
int take;
inline int Updiv(int a, int b) { return (a / b) + (a % b ? 1 : 0); }
struct ChairMan_Tree {
int T[N], sumv[maxnode], tot[maxnode], lc[maxnode], rc[maxnode], Size;
ChairMan_Tree () { Size = 0; }
void Update(int &o, int pre, int l, int r, int up) {
o = ++Size; lc[o] = lc[pre]; rc[o] = rc[pre];
sumv[o] = sumv[pre] + up; tot[o] = tot[pre] + 1;
if (l == r) return ;
int mid = (l + r) >> 1;
if (up <= mid) Update(lc[o], lc[pre], l, mid, up);
else Update(rc[o], rc[pre], mid + 1, r, up);
}
void Query(int s, int t, int l, int r, int val) {
if (l == r) { take += Updiv(val, l); return ; }
int here = tot[rc[t]] - tot[rc[s]], sv = sumv[rc[t]] - sumv[rc[s]], mid = (l + r) >> 1;
if (val > sv) { take += here; Query(lc[s], lc[t], l, mid, val - sv); }
else Query(rc[s], rc[t], mid + 1, r, val);
}
} CT;
int r, c, m;
int Mat[210][210];
int Sum[210][210][1010];
int Tot[210][210][1010];
int sum[N];
void Solve2() {
For (i, 1, c) {
int val = read();
sum[i] = sum[i - 1] + val;
CT.Update(CT.T[i], CT.T[i - 1], 1, 1000, val);
}
For (i, 1, m) {
read(); int l = read(); read(); int r = read(), h = read();
take = 0;
if (sum[r] - sum[l - 1] < h) { printf ("Poor QLW\n"); continue ; }
CT.Query(CT.T[l - 1], CT.T[r], 1, 1000, h);
printf ("%d\n", take);
}
}
inline int Calc_Sum (int xl, int yl, int xr, int yr, int low) {
return Sum[xr][yr][low] - Sum[xl - 1][yr][low] - Sum[xr][yl - 1][low] + Sum[xl - 1][yl - 1][low];
}
inline int Calc_Tot (int xl, int yl, int xr, int yr, int low) {
return Tot[xr][yr][low] - Tot[xl - 1][yr][low] - Tot[xr][yl - 1][low] + Tot[xl - 1][yl - 1][low];
}
void Solve1() {
For (i, 1, r)
For (j, 1, c) {
Mat[i][j] = read(); Sum[i][j][Mat[i][j]] += Mat[i][j]; ++Tot[i][j][Mat[i][j]];
For (k, 1, 1000) {
Sum[i][j][k] += Sum[i][j - 1][k] + Sum[i - 1][j][k] - Sum[i - 1][j - 1][k];
Tot[i][j][k] += Tot[i][j - 1][k] + Tot[i - 1][j][k] - Tot[i - 1][j - 1][k];
}
}
For (i, 1, r)
For (j, 1, c)
Fordown (k, 1000, 1) {
Sum[i][j][k] += Sum[i][j][k + 1];
Tot[i][j][k] += Tot[i][j][k + 1];
}
For (i, 1, m) {
int xi = read(), yi = read(), xj = read(), yj = read(), h = read();
int l = 1, r = 1000, need = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (Calc_Sum(xi, yi, xj, yj, mid) >= h) need = mid, l = mid + 1;
else r = mid - 1;
}
if (need == -1) { printf ("Poor QLW\n"); continue ; }
int Btot = Calc_Tot(xi, yi, xj, yj, need + 1),
Bsum = Calc_Sum(xi, yi, xj, yj, need + 1);
Btot += Updiv(h - Bsum, need);
printf ("%d\n", Btot);
}
}
int main () {
File();
r = read(); c = read(); m = read();
if (r == 1) Solve2(); else Solve1();
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】