题目链接
题意
给出一个长度为 n 的数列 A,接下来有m 次操作,操作有两种:
1 l r x,表示对i属于[l,r],令Ai = min(Ai,x)
2 l r k,表示询问区间[l,r]中第k小的数。
思路
第k小的数,也就是说小于第k小的有k-1个,考虑二分加分块,分块询问的时候求的是小于mid的数有多少个,二分逼近答案。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
int belong[maxn], l[2005], r[2005], tag[2005], num, block, n, q;
vector<int> v[2005];
int a[maxn];
void build()
{
block = sqrt(n);
num = n / block;
if(n % block) num++;
for (int i = 1; i <= num; i++)
l[i] = (i - 1) * block + 1, r[i] = i * block;
r[num] = n;
for (int i = 1; i <= n; i++) {
belong[i] = (i - 1) / block + 1;
v[belong[i]].push_back(a[i]);
}
for (int i = 1; i <= num; i++) {
tag[i] = INF;
sort(v[i].begin(), v[i].end());
}
}
void reset(int x)
{
v[x].clear();
for (int i = l[x]; i <= r[x]; i++) {
a[i] = min(a[i], tag[x]);
v[x].push_back(a[i]);
}
sort(v[x].begin(), v[x].end());
}
void update(int x, int y, int c) {
int tl = belong[x], tr = belong[y];
if(tl == tr) {
for (int i = x; i <= y; i++)
a[i] = min(a[i], c);
reset(tl);
return ;
}
for (int i = x; i <= r[tl]; i++)
a[i] = min(a[i], c);
reset(tl);
for (int i = l[tr]; i <= y; i++)
a[i] = min(a[i], c);
reset(tr);
for (int i = tl + 1; i < tr; i++)
tag[i] = min(tag[i], c);
}
int query(int x, int y, int c)
{
int tl = belong[x], tr = belong[y], ans = 0;
if(tl == tr) {
for (int i = x; i <= y; i++) {
if(a[i] <= c || tag[tl] <= c) ans++;
}
return ans;
}
for (int i = x; i <= r[tl]; i++) {
if(a[i] <= c || tag[tl] <= c) ans++;
}
for (int i = l[tr]; i <= y; i++) {
if(a[i] <= c || tag[tr] <= c) ans++;
}
for (int i = tl + 1; i < tr; i++) {
if(tag[i] <= c) ans += (r[i] - l[i] + 1);
else ans += upper_bound(v[i].begin(), v[i].end(), c) - v[i].begin();
}
return ans;
}
int check(int x, int y, int k) {
int left = -1e9, right = 1e9;
int ans;
while (left <= right) {
int mid = (left + right) / 2;
if(query(x, y, mid) >= k) {
ans = mid;
right = mid - 1;
}
else left = mid + 1;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
build();
for (int i = 1; i <= q; i++) {
int op, x, y, z;
scanf("%d%d%d%d", &op, &x, &y, &z);
if(op == 1) update(x, y, z);
else printf("%d\n", check(x, y, z));
}
}
乘法
题意
给A,B两个数集,求ai*bi的第K大的值。
思路
求第K大问题,用二分询问比mid大的数有多少个。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
ll a[maxn], b[maxn], k;
int n, m;
bool check(ll val) {
ll cnt = 0;
for (int i = 1; i <= n; i++) {
if(a[i] == 0) cnt += val < 0 ? m : 0;
else if(a[i] < 0) cnt += lower_bound(b + 1, b + 1 + m, ceil(1.0*val/a[i])) - b - 1;
else cnt += (m - (upper_bound(b + 1, b + 1 + m, floor(1.0 * val / a[i])) - b - 1));
}
return cnt <= k;
}
int main()
{
scanf("%d%d%lld", &n, &m, &k);
k--;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= m; i++) scanf("%lld", &b[i]);
sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + m);
ll left = -1e12, right = 1e12, ans;
while (left <= right) {
ll mid = (left + right) / 2;
if(check(mid)) {
ans = mid;
right = mid - 1;
}
else left = mid + 1;
}
printf("%lld\n", ans);
}