[SDOI2010]粟粟的书架
嘟嘟嘟
哈,二合一题目。
刚开始数据范围没看清,满脑子在想二维主席树。越想越觉得不可做。
后来仔细的读了题,才发现这题前后50%的数据对应不同的做法。
对于\(R = 1\)的数据,就是主席树板子。用主席树维护权值的后缀和,然后贪心往权值大的二分。
需要注意的是相同权值的可能有多本书,要取大于等于\(h\)的最少数量。
对于剩下50%。令f[i][j][k]表示\((1, 1)\)到\((i, j)\)的前缀矩阵中,权值大于等于\(k\)的书的高度之和;g[i][j][k]表示对应的数量之和。
然后二分,如果当前的和小于\(h\),就令L = mid - 1;否则R = mid。
同样需要注意的是相同高度的书可能有多个。还是要取大于\(h\)最少的书。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn1 = 205;
const int maxn2 = 5e5 + 5;
const int maxN = 1e3 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, m, q;
int a[maxn1][maxn1], Max = 0;
ll f[maxn1][maxn1][maxN];
int g[maxn1][maxn1][maxN];
In int solve(int xa, int ya, int xb, int yb, ll h)
{
int L = 0, R = Max, ret = 0;
while(L < R)
{
int mid = (L + R + 1) >> 1;
ll Sum = f[xb][yb][mid] - f[xb][ya - 1][mid] - f[xa - 1][yb][mid] + f[xa - 1][ya - 1][mid];
if(Sum < h) R = mid - 1;
else L = mid;
}
if(L == 0) return -1;
ret = g[xb][yb][L + 1] - g[xb][ya - 1][L + 1] - g[xa - 1][yb][L + 1] + g[xa - 1][ya - 1][L + 1];
h -= f[xb][yb][L + 1] - f[xb][ya - 1][L + 1] - f[xa - 1][yb][L + 1] + f[xa - 1][ya - 1][L + 1];
ll Sum = (f[xb][yb][L] - f[xb][ya - 1][L] - f[xa - 1][yb][L] + f[xa - 1][ya - 1][L]) - (f[xb][yb][L + 1] - f[xb][ya - 1][L + 1] - f[xa - 1][yb][L + 1] + f[xa - 1][ya - 1][L + 1]);
int num = (g[xb][yb][L] - g[xb][ya - 1][L] - g[xa - 1][yb][L] + g[xa - 1][ya - 1][L]) - (g[xb][yb][L + 1] - g[xb][ya - 1][L + 1] - g[xa - 1][yb][L + 1] + g[xa - 1][ya - 1][L + 1]);
ll pri = Sum / num;
return ret + (h + pri - 1) / pri;
}
In void work0()
{
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) a[i][j] = read(), Max = max(Max, a[i][j]);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
for(int k = 0; k <= Max; ++k)
{
f[i][j][k] = f[i][j - 1][k] + f[i - 1][j][k] - f[i - 1][j - 1][k];
g[i][j][k] = g[i][j - 1][k] + g[i - 1][j][k] - g[i - 1][j - 1][k];
}
for(int k = 0; k <= a[i][j]; ++k) f[i][j][k] += a[i][j], ++g[i][j][k];
}
for(int i = 1; i <= q; ++i)
{
int xa = read(), ya = read(), xb = read(), yb = read();
ll h = read();
int ans = solve(xa, ya, xb, yb, h);
if(ans < 0) puts("Poor QLW");
else write(ans), enter;
}
}
struct Tree
{
int ls, rs;
int num; ll sum;
}t[maxn2 * 20];
int root[maxn2], tcnt = 0;
In void insert(int old, int& now, int l, int r, int id)
{
t[now = ++tcnt] = t[old];
t[now].sum += id; ++t[now].num;
if(l == r) return;
int mid = (l + r) >> 1;
if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
else insert(t[old].rs, t[now].rs, mid + 1, r, id);
}
In int query(int old, int now, int l, int r, ll h)
{
if(!now && h) return -INF;
if(l == r)
{
ll Sum = t[now].sum - t[old].sum, pri = Sum / (t[now].num - t[old].num);
if(Sum < h) return -INF;
else return (h + pri - 1) / pri;
}
int mid = (l + r) >> 1;
ll Sum = t[t[now].rs].sum - t[t[old].rs].sum;
if(Sum > h) return query(t[old].rs, t[now].rs, mid + 1, r, h);
else if(Sum == h) return t[t[now].rs].num - t[t[old].rs].num;
else return t[t[now].rs].num - t[t[old].rs].num + query(t[old].ls, t[now].ls, l, mid, h - Sum);
}
int main()
{
n = read(), m = read(), q = read();
if(n ^ 1) {work0(); return 0;}
for(int i = 1, x; i <= m; ++i) x = read(), insert(root[i - 1], root[i], 0, maxN - 5, x);
for(int i = 1, L, R; i <= q; ++i)
{
read(), L = read(), read(), R = read();
ll h = read();
int ans = query(root[L - 1], root[R], 0, maxN - 5, h);
if(ans < 0) puts("Poor QLW");
else write(ans), enter;
}
return 0;
}