[国家集训队]矩阵乘法 整体二分
很好的一道题,令人大开眼界。
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 1000000
#define N 600
using namespace std;
int n,Q,idx,q[maxn],tl[maxn],tr[maxn],answer[maxn],cur[maxn];
struct MAT{
int x,y,w;
}mat[maxn];
int cmp(MAT a,MAT b){ return a.w<b.w;}
struct Query{
int a,b,c,d,k;
}ask[maxn];
struct BIT{
int C[N][N];
int lowbit(int x){ return x&(-x);}
void add(int x,int y,int k)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j)) C[i][j]+=k;
}
int query(int x,int y)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j)) ans+=C[i][j];
return ans;
}
int ask(Query a)
{
return query(a.c,a.d)-query(a.c,a.b-1)-query(a.a-1,a.d)+query(a.a-1,a.b-1);
}
}tree;
void solve(int x,int y,int l,int r){
if(x>y||l>r) return;
if(l==r){
for(int i=x;i<=y;++i) answer[q[i]]=mat[l].w;
return;
}
int mid=(l+r)>>1;
for(int i=l;i<=mid;++i) tree.add(mat[i].x,mat[i].y,1);
int left=0,right=0,s,u;
for(int i=x;i<=y;++i){
u=q[i];
s=cur[u]+tree.ask(ask[u]);
if(s>=ask[u].k) tl[++left]=u;
else tr[++right]=u,cur[u]=s;
}
int qcnt=x-1;
for(int i=1;i<=left;++i) q[++qcnt]=tl[i];
for(int i=1;i<=right;++i) q[++qcnt]=tr[i];
for(int i=l;i<=mid;++i) tree.add(mat[i].x,mat[i].y,-1);
solve(x,x+left-1,l,mid),solve(x+left,y,mid+1,r);
}
int main(){
//setIO("input");
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
mat[++idx].x=i,mat[idx].y=j;
scanf("%d",&mat[idx].w);
}
sort(mat+1,mat+1+idx,cmp);
for(int i=1;i<=Q;++i)
scanf("%d%d%d%d%d",&ask[i].a,&ask[i].b,&ask[i].c,&ask[i].d,&ask[i].k),q[i]=i;
solve(1,Q,1,idx);
for(int i=1;i<=Q;++i) printf("%d\n",answer[i]);
return 0;
}