The UVALIVE 7716 二维区间第k小

The UVALIVE 7716 二维区间第k小

 /**
题意:给一个n * n的矩阵,有q个查询
      每次查询r,c,s,k表示已(r,c)为右上角 大小为s的正方形中 第k小的元素
     n <= 250 ,q <= 250000 , a[i][j]<=10000
      
思路:用到了主席树求区间第k小,
      主席树本质是可持久化权值线段树,用区间[l,r]表示值在[l,r]间的数字有多少个
      每插入一个数字,实际上只修改了log个区间,其他的部分不发生变化,所以只需要对修改的区间新开结点即可
      求区间第k小,其实就是二分区间[l,r],m = l + r>>1
      如果权值[l,m]的数字个数少于k,那么答案就在[m+1,r]这个区间内
      否则就在[l,m]内,然后递归处理即可
      开始的做法是用以root[i][j]为根的树表示[1,1]到[i,j]的矩阵的信息,这样查询的复杂度为log(maxn)
      由于合并的复杂度不太科学,爆内存了
      然后改成以root[i][j]为根的树表示[i,1]到[i,j]区间的信息,空间复杂度为O(N * N * logN)
      然后我查询r,c,s,k的时候
      就把r行,r+1行,...r+s-1行的这些[c,c+s-1]区间一起操作,这样查询一次的复杂度为s * log(maxn)
*/

#include<bits/stdc++.h>
#define ls(i) seg[i].lc
#define rs(i) seg[i].rc
using namespace std;
typedef long long LL;
const int maxn = 1e4;
const int N = 280;
int n,q,rr,cc,ss,k;
int b[N][2];
struct node{
    int lc,rc,cnt;
}seg[N * N * 100];
int tot;
int root[N][N];
void update(int &rt,int l,int r,int pos,int val){
    seg[++tot] = seg[rt];
    rt = tot;
    seg[rt].cnt += val;
    if(l >= r) return ;
    int m = (l + r)>>1;
    if(pos <= m) update(ls(rt),l,m,pos,val);
    else update(rs(rt),m+1,r,pos,val);
}
int query(int l,int r,int k){
     if(l == r) return l;
     int m = (l + r)>>1;
     int cnt = 0;
     for(int i = 0;i < ss;i++) cnt += seg[ls(b[i][0])].cnt - seg[ls(b[i][1])].cnt;
     if(cnt < k) {
        for(int i = 0;i < ss;i++){
             b[i][0] = rs(b[i][0]);
             b[i][1] = rs(b[i][1]);
        }
        return  query(m+1,r,k - cnt);
     }
     for(int i = 0;i < ss;i++){
             b[i][0] = ls(b[i][0]);
             b[i][1] = ls(b[i][1]);
     }
     return  query(l,m,k);
}
int solve(){
   for(int i = 0;i < ss;i++){
        b[i][0] = root[rr + i][cc + ss - 1];
        b[i][1] = root[rr + i][cc - 1];
   }
   return query(1,maxn,k);
}
int main()
{
    while(scanf("%d",&n)==1){
        tot = 0;
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= n;j++){
                int x;
                scanf("%d",&x);
                root[i][j] = root[i][j-1];
                update(root[i][j],1,maxn,x,1);
            }
        }
        scanf("%d",&q);
        while(q--){
            scanf("%d%d%d%d",&rr,&cc,&ss,&k);
            ss = min(ss,min(n - rr + 1,n - cc + 1));
            k = min(k , ss * ss);
            printf("%d\n",solve());
        }
    }
    return 0;
}
posted @ 2017-07-31 21:47  jiachinzhao  阅读(245)  评论(0编辑  收藏  举报