1. 程式人生 > >BZOJ 2738 矩陣乘法 整體二分

BZOJ 2738 矩陣乘法 整體二分

題意:

  給出n*n的矩陣,求出子矩陣的k小值

分析:

  真理:當一個題的題目名是一個演算法時,這道題多半和這個演算法沒啥關係。

  其實就是把整體二分的步驟放到二維了。我們需要一個二維線段樹來查詢操作。

  其他嘛,直接把矩陣抻成序列即可。(但要記得記錄二維座標)

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int M=505;
const int N=60005;
int n,m,n2,tot,now;
struct node{int x,y,v;}v[M*M];
int q1[N],q2[N],q3[N],q4[N],qk[N];
int ans[N],s[M][M],p[N],q[N],sm[N]; bool cmp(node a,node b){ return a.v<b.v; } void plu(int x,int y,int val){ for(int i=x;i<=n;i+=i&-i) for(int j=y;j<=n;j+=j&-j) {s[i][j]+=val;} return ; } int query(int x,int y){ int r=0;for(int i=x;i;i-=i&-i) for(int j=y;j;j-=j&-j) {r
+=s[i][j];}return r; } void solve(int l,int r,int L,int R){ if(l>r) return ; if(L==R){ for(int i=l;i<=r;i++) {ans[p[i]]=v[L].v;}return ; } int mid=(L+R)>>1,md=l-1; while(now<mid) now++, plu(v[now].x,v[now].y,1); while(now>mid) plu(v[now].x,v[now].y,
-1),now--; for(int i=l;i<=r;i++){ sm[p[i]]=query(q1[p[i]]-1,q2[p[i]]-1)+ query(q3[p[i]],q4[p[i]])- query(q1[p[i]]-1,q4[p[i]])- query(q3[p[i]],q2[p[i]]-1); if(sm[p[i]]>=qk[p[i]]) md++; } int l1=l,l2=md+1; for(int i=l;i<=r;i++) if(sm[p[i]]>=qk[p[i]]) q[l1++]=p[i]; else q[l2++]=p[i]; for(int i=l;i<=r;i++) p[i]=q[i]; solve(l,md,L,mid);solve(md+1,r,mid+1,R); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&v[++n2].v),v[n2].x=i,v[n2].y=j; sort(v+1,v+1+n2,cmp); for(int i=1;i<=m;i++) scanf("%d%d%d%d%d",&q1[i],&q2[i],&q3[i], &q4[i],&qk[i]),p[i]=i;solve(1,m,1,n2); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }