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