题目描述
幸福幼儿园B29班的粟粟是一个聪明机灵、乖巧可爱的小朋友,她的爱好是画画和读书,尤其喜欢Thomas H. Cormen的文章。粟粟家中有一个R行C列的巨型书架,书架的每一个位置都摆有一本书,上数第i行、左数第j列摆放的书有Pi,j页厚。
粟粟每天除了读书之外,还有一件必不可少的工作就是摘苹果,她每天必须摘取一个指定的苹果。粟粟家果树上的苹果有的高、有的低,但无论如何凭粟粟自己的个头都难以摘到。不过她发现,如果在脚下放上几本书,就可以够着苹果;她同时注意到,对于第i天指定的那个苹果,只要她脚下放置书的总页数之和不低于Hi,就一定能够摘到。
由于书架内的书过多,父母担心粟粟一天内就把所有书看完而耽误了上幼儿园,于是每天只允许粟粟在一个特定区域内拿书。这个区域是一个矩形,第i天给定区域的左上角是上数第x1i行的左数第y1i本书,右下角是上数第x2i行的左数第y2i本书。换句话说,粟粟在这一天,只能在这﹙x2i-x1i+1﹚×﹙y2i-y1i+1﹚本书中挑选若干本垫在脚下,摘取苹果。
粟粟每次取书时都能及时放回原位,并且她的书架不会再撤下书目或换上新书,摘苹果的任务会一直持续M天。给出每本书籍的页数和每天的区域限制及采摘要求,请你告诉粟粟,她每天至少拿取多少本书,就可以摘到当天指定的苹果。
输入输出格式
输入格式:
输入文件susu.in第一行是三个正整数R, C, M。
接下来是一个R行C列的矩阵,从上到下、从左向右依次给出了每本书的页数Pi,j。
接下来M行,第i行给出正整数x1i, y1i, x2i, y2i, Hi,表示第i天的指定区域是﹙x1i, y1i﹚与﹙x2i, y2i﹚间的矩形,总页数之和要求不低于Hi。
保证1≤x1i≤x2i≤R,1≤y1i≤y2i≤C。
输出格式:
输出文件susu.out有M行,第i行回答粟粟在第i天时为摘到苹果至少需要拿取多少本书。如果即使取走所有书都无法摘到苹果,则在该行输出“Poor QLW”(不含引号)。
输入输出样例
输入样例#1: 复制
5 5 7
14 15 9 26 53
58 9 7 9 32
38 46 26 43 38
32 7 9 50 28
8 41 9 7 17
1 2 5 3 139
3 1 5 5 399
3 3 4 5 91
4 1 4 1 33
1 3 5 4 185
3 3 4 3 23
3 1 3 3 108
输出样例#1: 复制
6
15
2
Poor QLW
9
1
3
输入样例#2: 复制
1 10 7
14 15 9 26 53 58 9 7 9 32
1 2 1 9 170
1 2 1 9 171
1 5 1 7 115
1 1 1 10 228
1 4 1 4 45704571
1 1 1 1 1
1 7 1 8 16
输出样例#2: 复制
6
7
3
10
Poor QLW
1
2
说明
【数据规模和约定】
对于10%的数据,满足R, C≤10;
对于20%的数据,满足R, C≤40;
对于50%的数据,满足R, C≤200,M≤200,000;
另有50%的数据,满足R=1,C≤500,000,M≤20,000;
对于100%的数据,满足1≤Pi,j≤1,000,1≤Hi≤2,000,000,000。
#include <bits/stdc++.h>
#define mid (l + r) / 2
using namespace std;
const int mn = 5e5 + 10;
const int inf = 5e5 + 10;
int a[mn], tr[mn];
struct node
{
int sz, sum;
int ls, rs;
} trie[1000 * 40 * 200 + 10]; // 两百棵主席树
int cnt;
void build(int l, int r, int &id)
{
id = ++cnt;
if (l == r)
return;
build(l, mid, trie[id].ls);
build(mid + 1, r, trie[id].rs);
}
void Insert(int l, int r, int pre, int &now, int num)
{
now = ++cnt;
trie[now] = trie[pre];
trie[now].sz++;
trie[now].sum += num;
if (l == r)
return;
if (num <= mid)
Insert(l, mid, trie[pre].ls, trie[now].ls, num);
else
Insert(mid + 1, r, trie[pre].rs, trie[now].rs, num);
}
int x1, Y1, x2, y2;
int el[210], er[210];
int query(int l, int r, int h)
{
if (l == r)
return (h + l - 1) / l; // 向上取整
int rsz = 0, temp = 0;
for (int i = x1; i <= x2; i++)
{
rsz += trie[trie[er[i]].rs].sz - trie[trie[el[i]].rs].sz;
temp += trie[trie[er[i]].rs].sum - trie[trie[el[i]].rs].sum;
} // 每行第几大到第几大区间的增加值的和
if (temp >= h) // 右子树和足够 尽可能少的数 往右子树递归
{
for (int i = x1; i <= x2; i++)
{
el[i] = trie[el[i]].rs;
er[i] = trie[er[i]].rs;
// 左右界限分别对应于右子树的节点编号
}
return query(mid + 1, r, h);
}
else
{
for (int i = x1; i <= x2; i++)
{
el[i] = trie[el[i]].ls;
er[i] = trie[er[i]].ls;
// 左右界限分别对应于左子树的节点编号
}
return rsz + query(l, mid, h - temp);
}
}
int main()
{
build(1, 1000, tr[0]);
int r, c, m;
scanf("%d %d %d", &r, &c, &m);
int temp;
for (int i = 1; i <= r; i++)
{
for (int j = 1; j <= c; j++) // 每行维护一棵主席树
{
scanf("%d", &temp);
if (j == 1)
Insert(1, 1000, tr[0], tr[(i - 1) * c + j], temp); // 每行第一个点新创建主席树
else
Insert(1, 1000, tr[(i - 1) * c + j - 1], tr[(i - 1) * c + j], temp); // 在同行前一个点的基础上
}
}
while (m--)
{
int h;
scanf("%d %d %d %d %d", &x1, &Y1, &x2, &y2, &h);
int temp = 0;
for (int i = x1; i <= x2; i++)
{
if (Y1 == 1)
temp += trie[tr[(i - 1) * c + y2]].sum;
else
temp += trie[tr[(i - 1) * c + y2]].sum - trie[tr[(i - 1) * c + Y1 - 1]].sum;
} // 矩阵内数的总和
if (temp < h)
printf("Poor QLW\n");
else // 一定存在
{
for (int i = x1; i <= x2; i++)
{
if (Y1 == 1)
el[i] = tr[0];
else
el[i] = tr[(i - 1) * c + Y1 - 1];
er[i] = tr[(i - 1) * c + y2];
// 每行的区间左右节点
}
printf("%d\n", query(1, 1000, h));
}
}
return 0;
}
// 效华聚聚的
#include<bits/stdc++.h>
using namespace std;
int S[202][202][1005],t[202][202],a[202][1005];
int sum[50005][1002],c[500005];
int main()
{
int n,m,Q;
scanf("%d%d%d",&n,&m,&Q);
if(n==1)
{
for(int i=1;i<=m;i++)
{
scanf("%d",&c[i]);
if(i%10==0)
{
int len=i/10;
for(int j=1;j<=1000;j++) sum[len][j]=sum[len-1][j];
for(int j=i;j>10*(len-1);j--) sum[len][c[j]]++;
}
}
while(Q--)
{
int x1,y1,x2,y2,h;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int ans=0;
for(int i=1000;i>0;i--)
{
int s=sum[y2/10][i]-sum[y1/10][i];
for(int j=y2/10*10+1;j<=y2;j++) if(c[j]==i) s++;
for(int j=y1/10*10+1;j<y1;j++) if(c[j]==i) s--;
if(y1%10==0&&i==c[y1]) s++;
if(i*s<=h)
{
h-=i*s;
ans+=s;
}
else
{
ans+=(h+i-1)/i;
h=0;
}
if(h==0) break;
}
if(h>0) printf("Poor QLW\n");
else printf("%d\n",ans);
}
}
else
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&t[i][j]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=1000;k++)
{
if(t[i][j]==k) a[j][k]=a[j-1][k]+1;
else a[j][k]=a[j-1][k];
S[i][j][k]=S[i-1][j][k]+a[j][k];
}
}
}
while(Q--)
{
int x1,y1,x2,y2,h;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int ans=0;
for(int i=1000;i>0;i--)
{
int s=S[x2][y2][i]-S[x2][y1-1][i]-S[x1-1][y2][i]+S[x1-1][y1-1][i];
if(i*s<=h)
{
h-=i*s;
ans+=s;
}
else
{
ans+=(h+i-1)/i;
h=0;
}
if(h==0) break;
}
if(h>0) printf("Poor QLW\n");
else printf("%d\n",ans);
}
}
return 0;
}