【清澄A1333】【整体二分+二维树状数组】矩阵乘法(梁盾)

试题来源
  2012中国国家集训队命题答辩
问题描述
  给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。
输入格式
  第一行两个数N,Q,表示矩阵大小和询问组数;
  接下来N行N列一共N*N个数,表示这个矩阵;
  再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。
输出格式
  对于每组询问输出第K小的数。
样例输入
2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
样例输出
1
3
数据规模和约定
  矩阵中数字是10 9以内的非负整数;
  20%的数据:N<=100,Q<=1000;
  40%的数据:N<=300,Q<=10000;
  60%的数据:N<=400,Q<=30000;
  100%的数据:N<=500,Q<=60000。
【分析】
其实用可持久化的线段树应该也可以做。
裸的整体二分题,把每个输入的点按权值排个序就可以了。
其实可以加一点修改什么的,这样加着修改一起二分难度稍微大一点。
本周在校最后一题,哈哈哈..
  1 /*

  2 李商隐

  3 《无题·重帏深下莫愁堂》

  4 重帏深下莫愁堂,卧后清宵细细长。

  5 神女生涯原是梦,小姑居处本无郎。

  6 风波不信菱枝弱,月露谁教桂叶香。

  7 直道相思了无益,未妨惆怅是清狂。

  8 */

  9 #include <iostream>

 10 #include <cstdio>

 11 #include <algorithm>

 12 #include <cstring>

 13 #include <vector>

 14 #include <utility>

 15 #include <iomanip>

 16 #include <string>

 17 #include <cmath>

 18 #include <queue>

 19 #include <assert.h>

 20 #include <map>

 21 #include <ctime>

 22 #include <cstdlib>

 23 #include <stack>

 24 #define LOCAL

 25 const int MAXN = 500 + 10;

 26 const int MAXM = 60000 + 10;

 27 const int INF = 1000000000;

 28 const int SIZE = 450;

 29 const int maxnode =  250005 + 10;

 30 using namespace std;

 31 typedef long long ll;

 32 using namespace std;

 33 int n, m, cnt;

 34 int pos, Maxv;

 35 int c[MAXN][MAXN], Ans[MAXM];

 36 int id[MAXM];

 37 int tmp[MAXM];

 38 bool mark[MAXM];

 39 

 40 struct DATA{

 41     //横纵坐标和值 

 42     int x, y, val;

 43     bool operator < (const DATA &b)const {

 44          return val < b.val;

 45     }

 46 }data[maxnode];

 47 //问题 

 48 struct QUESTION{

 49     int x1, x2;

 50     int y1, y2, K;

 51 }q[MAXM];

 52 //输入 

 53 

 54 inline int lowbit(int x){return x & -x;}

 55 //插入 

 56 void add(int x, int y, int val){

 57     int f = y;

 58     while (x <= n){

 59           while (y <= n){

 60                 c[x][y] += val;

 61                 y += lowbit(y);

 62           }

 63           y = f;

 64           x += lowbit(x);

 65     } 

 66     return;

 67 }

 68 int sum(int x, int y){//

 69     int tmp = 0, f = y;

 70     while (x > 0){

 71           while (y > 0){

 72                 tmp += c[x][y];

 73                 y -= lowbit(y);

 74           }

 75           y = f;

 76           x -= lowbit(x);

 77     }

 78     return tmp;

 79 }

 80 //查询 

 81 int query(int k){

 82     int x1, x2, y1, y2;

 83     x1 = q[k].x1;x2 = q[k].x2;

 84     y1 = q[k].y1;y2 = q[k].y2;

 85     return sum(x2, y2) + sum(x1 - 1, y1 - 1) - sum(x1 - 1, y2) - sum(x2, y1 - 1);

 86 }

 87 //整体二分 

 88 void solve(int l, int r, int L, int R){

 89      if (l > r || L == R) return;//l和r是问题的编号 

 90      int mid = (L + R) >> 1;

 91      

 92      while (data[pos + 1].val <= mid && pos < cnt){//直接模拟 

 93            add(data[pos + 1].x, data[pos + 1].y, 1);

 94            pos++;

 95      }

 96      while (data[pos].val > mid){

 97            add(data[pos].x, data[pos].y , -1);

 98            pos--;

 99      }

100      int cnt = 0;

101      for (int i = l; i <= r; i++){

102          if (query(id[i]) > q[id[i]].K - 1){

103             mark[i] = 1;

104             Ans[id[i]] = mid;

105             cnt++;

106          }else mark[i] = 0;

107      } 

108      int l1 = l, l2 = l + cnt;

109      for (int i = l; i <= r; i++)

110      if (mark[i]) tmp[l1++] = id[i];

111      else tmp[l2++] = id[i];

112      //分成两部分继续整体二分 

113      for (int i = l; i <= r; i++) id[i] = tmp[i];

114      solve(l, l1 - 1, L, mid);

115      solve(l1, l2 - 1, mid + 1, R);

116 }

117 

118 void init(){

119     memset(mark, 0, sizeof(mark));

120     memset(c, 0, sizeof(c));

121     scanf("%d%d", &n, &m);

122     Maxv = 0;

123     cnt = 0;

124     for (int i = 1; i <= n; i++)

125     for (int j = 1; j <= n; j++){

126         scanf("%d", &data[++cnt].val);

127         data[cnt].x = i;//横纵坐标 

128         data[cnt].y = j;

129         Maxv = max(data[cnt].val, Maxv);

130     }

131     sort(data + 1, data + 1 + cnt); 

132 }

133 void work(){

134      for (int i = 1; i <= m; i++){

135          scanf("%d%d%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2, &q[i].y2, &q[i].K);

136      }

137      for (int i = 1; i <= m; i++) id[i] = i;//问题序列

138      solve(1, m, 0, Maxv + 1);

139      for (int i = 1; i <= m; i++) printf("%d\n", Ans[i]); 

140 }

141 

142 int main(){

143     

144     init();

145     work();

146     return 0;

147 }
View Code

 

你可能感兴趣的:(树状数组)