矩阵乘法 BZOJ 2738

矩阵乘法

【问题描述】

给你一个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

【样例说明】

矩阵中数字是109以内的非负整数;
20%的数据:N<=100,Q<=1000;
40%的数据:N<=300,Q<=10000;
60%的数据:N<=400,Q<=30000;
100%的数据:N<=500,Q<=60000。


题解:

将每个点储存下来,排序一下,和询问进行二分

我们将小于等于当前枚举的答案(即为mid)的点加入树状数组

对于区间内的询问,查询子矩阵内的小于等于mid的个数,如果大于等于这个询问要求的k,将其放置在左区间,表示第k小在l到mid之间

否则放置在右区间,表示第k小在mid+1到r之间

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 using namespace std;
 8 inline void Scan(int &x)
 9 {
10     char c;
11     while((c = getchar()) < '0' || c > '9');
12     x = c - '0';
13     while((c = getchar()) >= '0' && c <= '9')
14         x = x * 10 + c - '0';
15 }
16 const int maxn = 1233;
17 const int maxq = 300233;
18 struct dot
19 {
20     int x, y, v;
21 };
22 dot c[maxq];
23 struct ask
24 {
25     int x, y, a, b, c;
26 };
27 ask a[maxq];
28 int n, q;
29 int p;
30 int num;
31 int ans[maxq];
32 int id[maxq], tmp[maxq];
33 bool lr[maxq];
34 int tr[maxn][maxn];
35 inline void Ins(int x, int y, int z)
36 {
37     for(int i = x; i <= n; i += i & -i)
38         for(int j = y; j <= n; j += j & -j)
39             tr[i][j] += z;
40 }
41 inline int Ask(int x, int y)
42 {
43     int sum = 0;
44     for(int i = x; i; i -= i & -i)
45         for(int j = y; j; j -= j & -j)
46             sum += tr[i][j];
47     return sum;
48 }
49 inline void Two(int x, int y, int l, int r)
50 {
51     if(x > y) return;
52     if(l == r)
53     {
54         for(int i = x; i <= y; ++i) ans[id[i]] = r;
55         return;
56     }
57     int mi = l + r >> 1;
58     while(c[p + 1].v <= mi) ++p, Ins(c[p].x, c[p].y, 1);
59     while(c[p].v > mi) Ins(c[p].x, c[p].y, -1), --p;
60     int tot, cnt = 0;
61     for(int i = x; i <= y; ++i)
62     {
63         int k = id[i];
64         tot = Ask(a[k].a, a[k].b) - Ask(a[k].a, a[k].y - 1) - Ask(a[k].x - 1, a[k].b) + Ask(a[k].x - 1, a[k].y - 1);
65         if(tot >= a[k].c) lr[i] = true, ++cnt;
66         else lr[i] = false;
67     }
68     int le = x - 1, ri = x + cnt - 1;
69     for(int i = x; i <= y; ++i)
70         if(lr[i]) tmp[++le] = id[i];
71         else tmp[++ri] = id[i];
72     for(int i = x; i <= y; ++i) id[i] = tmp[i];
73     Two(x, le, l, mi), Two(le + 1, ri, mi + 1, r);
74 }
75 inline bool rule(dot a, dot b)
76 {
77     return a.v < b.v;
78 }
79 int main()
80 {
81     Scan(n), Scan(q);
82     int val;
83     for(int i = 1; i <= n; ++i)
84         for(int j = 1; j <= n; ++j)
85         {
86             Scan(val);
87             c[++num] = (dot) {i, j, val};
88         }
89     sort(c + 1, c + 1 + num, rule);
90     for(int i = 1; i <= q; ++i)
91         Scan(a[i].x), Scan(a[i].y), Scan(a[i].a), Scan(a[i].b), Scan(a[i].c), id[i] = i;
92     Two(1, q, 0, c[num].v);
93     for(int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
94 }

转载于:https://www.cnblogs.com/lytccc/p/6544754.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值