如何在有序矩阵中找第k大的数?
378. 有序矩阵中第K小的元素 题目链接
一个矩阵从左到右递增、从上到下递增怎么找第k大的数?
1、 把矩阵变成一维然后排个序
2、 合并有序数组那样合并
3、 二分
主要说二分怎么求。
二分一个第k大是多少。
check的时候,也就是判断有没有k个数大于等于mid
比mid小的肯定在是左上角一些元素。
比如这样一个矩阵:
如果mid是12的话:
就有左上角这样一些:
这样的话就可以用n+n的时间快速统计出来有多少个数大于等于mid!!
(明明可以暴力的)
int sum = 0;
int j = 0;
int i = n - 1;
while(i >= 0 && j < n)
{
if(a[i][j] > x)
i -- ;
else
{
sum += i + 1;
j ++ ;
}
}
然后用二分来写就好了
代码:
class Solution {
public:
int n;
int flag;
bool pd(int x,vector<vector<int>>& a)
{
int sum = 0;
int j = 0;
int i = n - 1;
while(i >= 0 && j < n)
{
if(a[i][j] > x)
i -- ;
else
{
sum += i + 1;
j ++ ;
}
}
return sum >= flag;
}
int kthSmallest(vector<vector<int>>& a, int k) {
flag = k;
n = a.size();
int l = a[0][0];
int r = a[n - 1][n - 1];
while(l < r)
{
int mid = l + r >> 1;
if(pd(mid, a))
r = mid;
else
l = mid + 1;
}
return l;
}
};
然后来看这道题 1508. 子数组和排序后的区间和
题目大意:
有一个数组,计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 n * (n + 1) / 2 个数字的数组。然后将他们排序,求排完序之后 l ~ r 的和。
这个n只有1000,所以也可以暴力,
lt学长问n有100000的时候怎么处理,我不会了,我好菜
题解
看这篇题解会的:
题解地址
f(k)表示排完序后求前k项的和
所以答案就是f® - f(l - 1)。
先用数组构造一个矩阵:
a[i][j]代表原数组中 i ~ j 的元素的和。
这个矩阵只有右上角有值。
求的就是这个矩阵中前k小的值得和。
所以找到第k小,然后求比nk小的元素得所有和就好了。
注意到构造的这个矩阵也是一个有序的,因为是前缀和得矩阵。
这个矩阵不用完全构造出来。因为第i行j列得元素就是sum[j] - sum[i - 1]
然后用上面的方法求一个第k小就好了
代码:
#include<algorithm>
#include<iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <stack>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){
freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){
return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){
a %= mod;ll ans = 1;while(b){
if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{
bool operator()(const pii & a, const pii & b){
return a.second < b.second;}};
int lb(int x){
return x & -x;}
//friend bool operator < (Node a,Node b) 重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int maxn = 1e5+10;
const int M = 100000;
class Solution {
public:
ll sum[maxn];
ll ss[maxn];
int n;
bool pd(int x,int flag)
{
int num = 0;
int r = 1;
for (int i= 1; i <= n; i ++ )
{
while(r <= n && sum[r] - sum[i - 1] <= x)
r ++ ;
num += r - i;
}
return num >= flag;
}
int getk(int x)
{
int l = 0, r = 1e5;// 因为数据大小小。。
while(l < r)
{
int mid = l + r >> 1;
if(pd(mid,x))
{
r = mid;
}
else
l = mid + 1;
}
return l;
}
ll getans(int x)
{
int num = getk(x);//第k大的数是num
// printf("%d %d\n",x,num);
int r = 1;
ll ans = 0;
int s =0 ;
for (int i = 1; i <= n; i ++ )
{
while(r <= n && sum[r] - sum[i - 1] < num)
r++ ;
int cnt = r - i;
s += cnt;
ans += (ss[r - 1] - ss[i - 1] - 1ll * cnt * sum[i - 1])%mod;
}
ans += 1ll * num * (x - s) % mod;
ans %= mod;
return ans;
}
int rangeSum(vector<int>& a, int np, int left, int right) {
n = np;
for (int i = 1; i <= n; i ++ )
sum[i] = sum[i - 1] + a[i - 1];
for (int i = 1; i <= n; i ++ )
{
ss[i] = (ss[i - 1] + sum[i])%mod;
}
ll s1 = getans(right);
ll s2 = getans(left - 1);
// printf("%lld %lld\n",s1,s2);
return (s1 - s2 + mod) % mod;
}
};
// int main()
// {
// /* code */
// int n;
// scanf("%d",&n);
// std::vector<int> v;
// for (int i= 1; i <= n; i ++ )
// {
// int x;
// scanf("%d",&x);
// v.pb(x);
// }
// int l,r;
// scanf("%d%d",&l,&r);
// cout<<asd.rangeSum(v,n,l,r);
// return 0;
// }
/* 4 1 2 3 4 1 5 */