应该是自己做的第一道组合题了吧,不过因为我比较菜,所以只是二合一
我们看完题目应该就能抽象出问题的本质:在一个子矩形中至少选取多少个数才能使得它们的和大于等于某个给定值。
朴素的想法:经典的二维前缀和+容斥问题,考虑求出以下两个数组:
-
(num_{i,j,k}),表示以((i,j))为右下角且矩阵中(ge k)的数的个数
-
(sum_{i,j,k}),表示以((i,j))为右下角且矩阵中(ge k)的数的总和
对于50%的数据,满足R, C≤200,M≤200,000
所以我们直接暴力三维循环是可以直接求出来的小心空间不要开爆了
考虑对于每一次询问,我们都通过二分求出那个最小的(k),即可间接得出答案。
然后考虑优化,抱歉我不会了,我们再仔细看这道题的数据有一个很奇怪的地方:
另有50%的数据,满足R=1,C≤500,000,M≤20,000;
这不是一行数吗,那么就是序列上的问题了。
这个就很套路了,由于没有修改我们发现上主席树是一个十分明智的选择。
主席树维护每一个权值区间内数的个数以及总和,不过如果再直接二分就可能会T掉了
所以我们要在主席树上二分,我们每一次优先考虑右子树,能拿肯定先把右子树给拿光。
所以就写完了,码量也不算很大吧。
CODE
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const int R=205;
int r,c,m,q,ans,p[R][R],mx,opt,x1,x2,y1,y2,s;
bool flag;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+'0');
}
namespace Square_sum
{
const int H=1005;
int sum[R][R][H],num[R][R][H],p[R][R],mx=0;
inline void init(void)
{
register int i,j,k;
for (i=1;i<=r;++i)
for (j=1;j<=c;++j)
read(p[i][j]),mx=p[i][j]>mx?p[i][j]:mx;
for (k=1;k<=mx;++k)
for (i=1;i<=r;++i)
for (j=1;j<=c;++j)
{
sum[i][j][k]=sum[i][j-1][k]+sum[i-1][j][k]-sum[i-1][j-1][k]+(p[i][j]>=k?p[i][j]:0);
num[i][j][k]=num[i][j-1][k]+num[i-1][j][k]-num[i-1][j-1][k]+(p[i][j]>=k);
}
}
inline int get_sum(int x1,int y1,int x2,int y2,int k)
{
return sum[x2][y2][k]-sum[x1-1][y2][k]-sum[x2][y1-1][k]+sum[x1-1][y1-1][k];
}
inline int get_num(int x1,int y1,int x2,int y2,int k)
{
return num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k]+num[x1-1][y1-1][k];
}
inline void solve(void)
{
if (get_sum(x1,y1,x2,y2,1)<s) { puts("Poor QLW"); return; }
int l=1,r=mx,mid,res;
while (l<=r)
{
mid=l+r>>1;
if (get_sum(x1,y1,x2,y2,mid)>=s) res=mid,l=mid+1; else r=mid-1;
}
write(get_num(x1,y1,x2,y2,res)-(get_sum(x1,y1,x2,y2,res)-s)/res); putchar('
');
}
};
namespace President_tree
{
const int N=500005,H=1000;
struct President_tree
{
int ch[2],size,sum;
}node[N*11];
int rt[N],tot;
inline void build(int &now,int l,int r)
{
now=++tot; if (l==r) return;
int mid=l+r>>1; build(node[now].ch[0],l,mid); build(node[now].ch[1],mid+1,r);
}
inline void insert(int lst,int &now,int l,int r,int x)
{
now=++tot; node[now]=node[lst];
++node[now].size; node[now].sum+=x;
if (l==r) return; int mid=l+r>>1;
if (x<=mid) insert(node[lst].ch[0],node[now].ch[0],l,mid,x);
else insert(node[lst].ch[1],node[now].ch[1],mid+1,r,x);
}
inline int query(int lst,int now,int l,int r,int s)
{
if (l==r) return (s+l-1)/l;
int mid=l+r>>1,dlt=node[node[now].ch[1]].sum-node[node[lst].ch[1]].sum;
if (dlt>=s) return query(node[lst].ch[1],node[now].ch[1],mid+1,r,s);
else return query(node[lst].ch[0],node[now].ch[0],l,mid,s-dlt)+node[node[now].ch[1]].size-node[node[lst].ch[1]].size;
}
inline void init(void)
{
register int i; int x; build(rt[0],1,H);
for (register int i=1;i<=c;++i)
read(x),insert(rt[i-1],rt[i],1,H,x);
}
inline void solve(void)
{
if (node[rt[y2]].sum-node[rt[y1-1]].sum<s) { puts("Poor QLW"); return; }
write(query(rt[y1-1],rt[y2],1,H,s)); putchar('
');
}
};
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j; read(r); read(c); read(m);
if (r^1) Square_sum::init(),opt=1; else President_tree::init();
while (m--)
{
read(x1); read(y1); read(x2); read(y2); read(s);
if (opt) Square_sum::solve(); else President_tree::solve();
}
return 0;
}