[SDOI2010] 粟粟的书架
给定一个 \(n\times m\) 的矩阵 \(A\)。
有 \(q\) 次询问,每次询问给定 \(x_1\),\(y_1\),\(x_2\),\(y_2\),\(H\),求在子矩阵 \((x_1,y_1)\rightarrow (x_2,y_2)\) 中选择至少多少个元素,才能使所有元素和为 \(H\)。
数据范围:
对于前 \(50\%\) 的数据,满足 \(n,m\le200\),\(q\le2\times 10^5\)。对于另外 \(50\%\) 的数据,满足 \(n=1,m\le5\times10^5\),\(m\le 2\times10^4\)。
观察数据范围,发现可以拆分为两道问题。
前 50 分
首先显然从大往小选。
考虑二分选的数中的最小数 \(k\),统计给定子矩阵中所有 \(\ge k\) 的数的和 \(S\) 和数量 \(C\)。
- 若 \(S\ge H\),则成立,提高 \(k\)。
- 反之,则降低 \(k\)。
单调性:若 \(k\) 增加,合法数减少,\(S\) 降低,所以存在某点 \(k'\) 使得比 \(k'\) 大的答案不合法,小的均合法。
最终答案:\(C-res\),其中 \(res\) 表示多选的 \(k\),即 \(res=\lfloor\cfrac{(S-H)}{k}\rfloor\)。
\(S\) 和 \(C\) 通过前缀和维护即可。
namespace Denia1 { // n = 1的情况
const int maxn = 200;
const int maxm = 1e3;
int p[maxn + 5][maxn + 5];
int pl[maxn + 5][maxn + 5];
int x[maxn * maxn + 5],xcnt = 0,tot = 0;
ll sum[maxm + 5][maxn + 5][maxn + 5];
int cnt[maxm + 5][maxn + 5][maxn + 5];
ll SUM(int k,int sx,int sy,int ex,int ey) {
return sum[k][ex][ey] - sum[k][sx - 1][ey] - sum[k][ex][sy - 1] + sum[k][sx - 1][sy - 1];
}
ll CNT(int k,int sx,int sy,int ex,int ey) {
return cnt[k][ex][ey] - cnt[k][sx - 1][ey] - cnt[k][ex][sy - 1] + cnt[k][sx - 1][sy - 1];
}
void Main() {
for (int i = 1;i <= n;i ++) {
for (int j = 1;j <= m;j ++) {
cin >> p[i][j];
x[++ xcnt] = p[i][j];
}
}
sort(x + 1,x + xcnt + 1);
tot = unique(x + 1,x + xcnt + 1) - x - 1;
for (int i = 1;i <= n;i ++) for (int j = 1;j <= m;j ++) pl[i][j] = lower_bound(x + 1,x + tot + 1,p[i][j]) - x;
for (int k = 0;k <= tot;k ++) {
for (int i = 1;i <= n;i ++) {
for (int j = 1;j <= m;j ++) {
ll v = 0ll; int c = 0;
if (pl[i][j] >= k) v = p[i][j],c = 1;
sum[k][i][j] = v + sum[k][i - 1][j] + sum[k][i][j - 1] - sum[k][i - 1][j - 1];
cnt[k][i][j] = c + cnt[k][i - 1][j] + cnt[k][i][j - 1] - cnt[k][i - 1][j - 1];
}
}
}
while (q --) {
int sx,sy,ex,ey; ll h; cin >> sx >> sy >> ex >> ey >> h;
int L = 0,R = tot,ans = 0;
while (L <= R) {
int mid = (L + R) >> 1;
if (SUM(mid,sx,sy,ex,ey) >= h) L = mid + 1,ans = mid;
else R = mid - 1;
}
ll v = SUM(ans,sx,sy,ex,ey);
if (v < h) cout << "Poor QLW\n";
else {
int c = CNT(ans,sx,sy,ex,ey);
int fl = 0;
if (x[ans] != 0) fl = (v - h) / x[ans];
cout << c - fl << "\n";
}
}
}
}
后 50 分
套主席树板子 + 线段树二分。
namespace Denia2 { // n = 1的情况
const int maxn = 5e5;
int p[maxn + 5];
int pl[maxn + 5],tot = 0;
struct PST {
#define ls(x) (tr[x].ls)
#define rs(x) (tr[x].rs)
struct node {
int ls,rs;
ll sum;
int cnt;
};
int root[maxn + 5];
node tr[40 * maxn + 5]; int nodecnt = 0;
int build(int l,int r) {
int u = ++ nodecnt;
if (l == r) return u;
int mid = (l + r) >> 1;
ls(u) = build(l,mid);
rs(u) = build(mid + 1,r);
return u;
}
int insert(int rt,int l,int r,int p,ll v) {
int u = ++ nodecnt;
tr[u] = tr[rt];
if (l == r) return tr[u].sum += v,tr[u].cnt ++,u;
int mid = (l + r) >> 1;
if (p <= mid) ls(u) = insert(ls(u),l,mid,p,v);
else rs(u) = insert(rs(u),mid + 1,r,p,v);
tr[u].sum = tr[ls(u)].sum + tr[rs(u)].sum;
tr[u].cnt = tr[ls(u)].cnt + tr[rs(u)].cnt;
return u;
}
// 查询和>=qs的最少元素数量
int query(int u,int v,int l,int r,ll qs) {
if (l == r) return (qs + pl[l] - 1) / pl[l];
int mid = (l + r) >> 1;
ll rsum = (tr[rs(v)].sum - tr[rs(u)].sum);
if (qs <= rsum) return query(rs(u),rs(v),mid + 1,r,qs);
return query(ls(u),ls(v),l,mid,qs - rsum) + tr[rs(v)].cnt - tr[rs(u)].cnt;
}
};
PST pst;
void Main() {
for (int i = 1;i <= m;i ++) cin >> p[i],pl[i] = p[i];
sort(pl + 1,pl + m + 1);
tot = unique(pl + 1,pl + m + 1) - pl - 1;
for (int i = 1;i <= m;i ++) p[i] = lower_bound(pl + 1,pl + tot + 1,p[i]) - pl;
pst.root[0] = pst.build(1,tot);
for (int i = 1;i <= m;i ++) pst.root[i] = pst.insert(pst.root[i - 1],1,tot,p[i],pl[p[i]]);
while (q --) {
int x1,l,x2,r; ll h; cin >> x1 >> l >> x2 >> r >> h;
ll sums = pst.tr[pst.root[r]].sum - pst.tr[pst.root[l - 1]].sum;
if (sums < h) cout << "Poor QLW\n";
else cout << pst.query(pst.root[l - 1],pst.root[r],1,tot,h) << "\n";
}
}
}

浙公网安备 33010602011771号