Address
Solution
Problem1 对于50%的数据,满足
r,c≤200,m≤2×105
- 问题的一种暴力解决方案显然就是把询问矩阵中的所有书按页数排序,然后不断把页数多的书取出来,直到取出来的总页数
≥hi
。
- 考虑到
pi,j≤1000
,我们定义两个数组
val[i][j][k],cnt[i][j][k]
。其中,
-
cnt[i][j][k]
表示左上角为
(1,1)
,右下角为
(i,j)
的矩形中总共有多少本满足 页数
≥k
的书。
-
val[i][j][k]
表示左上角为
(1,1)
,右下角为
(i,j)
的矩形中满足 页数
≥k
的书总共有多少页。
- 这样我们就可以求出
O(1)
求出询问矩阵中大于某个页数的书的数量及总页数。
- 于是考虑二分,每次二分取出的最小页数(根据暴力的解决方案,大于最小页数的显然都要全部取,我们希望最小页数尽量大,这样取的书也会尽量少),利用
val
数组来判断合法缩小边界,答案用
cnt
数组来计算。
- 注意和最小页数页数相同的书不一定要全部取,可以直接算出来要取多少个,从而调整答案。
- 时间复杂度
O(mlog1000+r×c×1000)
。
Problem2 另有50%的数据,满足
r=1,c≤5×105,m≤2×104
- 因为
r=1
,这就变成了一个序列问题。
- 参考前50分的解题过程,我们不难想到用主席树来维护,就能够很方便地求出在询问区间中页数大于等于某个数的书本本数和总页数。
- 回忆求区间第
K
大数的过程,我们同样可以在权值线段树上二分取出的最小页数,但也要注意相同页数的问题,最后走到叶子节点调整下答案。
- 时间复杂度 O((c+m)log1000)。
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
namespace INOUT
{
const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;
inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = nxtChar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
inline void put(int x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}
};
using namespace INOUT;
const int N = 205;
int n, m, r, c, lim, p[N][N];
int bx, by, ex, ey, h;
inline void CkMax(int &x, int y) {if (x < y) x = y;}
namespace solve1
{
const int L = 5e5 + 5;
int rt[L], p1[L], T;
struct Chair
{
int lc, rc, cnt, val;
#define l(x) tr[x].lc
#define r(x) tr[x].rc
#define c(x) tr[x].cnt
#define v(x) tr[x].val
}tr[L * 15];
inline void Insert(int &x, int y, int l, int r, int vi)
{
tr[x = ++T] = tr[y]; ++c(x); v(x) += vi;
if (l == r) return ;
int mid = l + r >> 1;
if (vi <= mid)
Insert(l(x), l(y), l, mid, vi);
else
Insert(r(x), r(y), mid + 1, r, vi);
}
inline int Query(int x, int y, int l, int r, int k)
{
if (l == r)
{
int num = 0;
while (k > 0) k -= l, ++num;
return num;
}
int mid = l + r >> 1, w = v(r(y)) - v(r(x));
if (k > w)
return Query(l(x), l(y), l, mid, k - w) + c(r(y)) - c(r(x));
else
return Query(r(x), r(y), mid + 1, r, k);
}
inline void work()
{
l(0) = r(0) = c(0) = v(0) = 0;
for (int i = 1; i <= c; ++i)
CkMax(lim, p1[i] = get());
for (int i = 1; i <= c; ++i)
Insert(rt[i], rt[i - 1], 1, lim, p1[i]);
while (m--)
{
bx = get(); by = get() - 1;
ex = get(); ey = get(); h = get();
if (v(rt[ey]) - v(rt[by]) < h)
puts("Poor QLW");
else
put(Query(rt[by], rt[ey], 1, lim, h)), putchar('\n');
}
}
};
namespace solve2
{
const int M = 1005;
int val[N][N][M], cnt[N][N][M];
inline int MatrixVal(int mi)
{
return val[ex][ey][mi] - val[bx][ey][mi] - val[ex][by][mi] + val[bx][by][mi];
}
inline int MatrixCnt(int mi)
{
return cnt[ex][ey][mi] - cnt[bx][ey][mi] - cnt[ex][by][mi] + cnt[bx][by][mi];
}
inline void work()
{
for (int i = 1; i <= r; ++i)
for (int j = 1; j <= c; ++j)
CkMax(lim, p[i][j] = get());
for (int i = 1; i <= r; ++i)
for (int j = 1; j <= c; ++j)
for (int k = 1; k <= p[i][j]; ++k)
val[i][j][k] += p[i][j], ++cnt[i][j][k];
for (int i = 1; i <= r; ++i)
for (int j = 1; j <= c; ++j)
for (int k = 1; k <= lim; ++k)
val[i][j][k] += val[i][j - 1][k], cnt[i][j][k] += cnt[i][j - 1][k];
for (int j = 1; j <= c; ++j)
for (int i = 1; i <= r; ++i)
for (int k = 1; k <= lim; ++k)
val[i][j][k] += val[i - 1][j][k], cnt[i][j][k] += cnt[i - 1][j][k];
while(m--)
{
bx = get() - 1; by = get() - 1;
ex = get(); ey = get(); h = get();
if (MatrixVal(1) < h)
puts("Poor QLW");
else
{
int l = 1, r = lim, Ans = 0;
while (l <= r)
{
int mid = l + r >> 1;
if (MatrixVal(mid) >= h)
Ans = mid, l = mid + 1;
else
r = mid - 1;
}
int Sans = MatrixCnt(Ans), Sval = MatrixVal(Ans),
num = Sans - MatrixCnt(Ans + 1);
while (num && Sval - Ans >= h) Sval -= Ans, --num, --Sans;
put(Sans), putchar('\n');
}
}
}
};
int main()
{
freopen("susu.in", "r", stdin);
freopen("susu.out", "w", stdout);
r = get(); c = get(); m = get();
if (r == 1)
solve1::work();
else
solve2::work();
fclose(stdin); fclose(stdout);
return 0;
}