一、题目
二、解法
整体二分的经典题。
把询问和修改(包括初始值)按时间顺序排好,我们维护区间 [ q l , q r ] [ql,qr] [ql,qr]的答案为 [ l , r ] [l,r] [l,r],每次取一个中间值 m i d mid mid,扫描区间,如果是修改,考虑修改的值是不是大于 m i d mid mid,如果大于 m i d mid mid在二维树状数组上修改,插入右边;否则直接插入左边。
如果是询问,考虑查出来的个数如果小于 k k k的大小,那么把 k k k减去这个个数之后归类到左边;否则归类到右边。然后左边的答案是 [ l , m i d ] [l,mid] [l,mid],右边的答案是 [ m i d + 1 , r ] [mid+1,r] [mid+1,r],当 l = r l=r l=r的时候就找到答案了。
我的写法是求 k k k大,所以把 k k k小转化成 k k k大就可以了,我偷了个懒,二分值域的时候直接分 [ 0 , 1 e 9 ] [0,1e9] [0,1e9]是会 T T T的(但是加了 O 2 O2 O2能过),读者对自己要求高的话建议写一下离散化或者二分出现了的值哦。
#include <cstdio>
const int M = 320005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,ans[M],bit[505][505];
struct node
{
int x1,y1,x2,y2,v,id;
}a[M],L[M],R[M];
int lowbit(int x)
{
return x&(-x);
}
void upd(int x,int y,int f)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
bit[i][j]+=f;
}
int ask(int x,int y)
{
int r=0;
for(int i=x;i>=1;i-=lowbit(i))
for(int j=y;j>=1;j-=lowbit(j))
r+=bit[i][j];
return r;
}
int get(int x1,int y1,int x2,int y2)
{
return ask(x2,y2)+ask(x1-1,y1-1)-ask(x2,y1-1)-ask(x1-1,y2);
}
void cdq(int l,int r,int ql,int qr)
{
if(qr<ql || l>r) return ;
if(l==r)
{
for(int i=ql;i<=qr;i++) ans[a[i].id]=l;
return ;
}
int mid=(l+r)>>1,tl=0,tr=0;
for(int i=ql;i<=qr;i++)
{
if(a[i].id==0)
{
if(a[i].v>mid) upd(a[i].x1,a[i].y1,1),R[++tr]=a[i];
else L[++tl]=a[i];
}
else
{
int t=get(a[i].x1,a[i].y1,a[i].x2,a[i].y2);
if(a[i].v>t) a[i].v-=t,L[++tl]=a[i];
else R[++tr]=a[i];
}
}
for(int i=ql;i<=qr;i++)
if(a[i].id==0 && a[i].v>mid)
upd(a[i].x1,a[i].y1,-1);
for(int i=1;i<=tl;i++) a[ql+i-1]=L[i];
for(int i=1;i<=tr;i++) a[ql+tl+i-1]=R[i];
cdq(l,mid,ql,ql+tl-1);
cdq(mid+1,r,ql+tl,qr);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[++k]=node{i,j,0,0,read(),0};
for(int i=1;i<=m;i++)
{
int x1=read(),y1=read(),x2=read(),y2=read(),v=read();
v=(x2-x1+1)*(y2-y1+1)-v+1;
a[++k]=node{x1,y1,x2,y2,v,i};
}
cdq(0,1e9,1,k);
//这里是需要卡常的哟,作者偷懒了(但是O2能过
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}