step 1
더보기
원래 있는 수열을 쿼리로 변환해 봅시다.그럼 문제는 다음과 같이 바뀝니다.
길이 $N$인 수열이 주어집니다. 처음 수열의 값은 전부 0입니다.
- $1 i k$ : 수열 $i$번째 위치에 $k$를 추가합니다.
- $2 i j k$ : $A_i, A_{i+1}, ..., A_j$로 이루어진 부분 수열 중에서 $k$보다 큰 원소의 개수를 출력한다.
step 2
더보기
$2 i j k$를 봅시다. 수열에서 $k$ 이하인 숫자들은 아무 의미가 없습니다. $k$ 초과인 숫자들은 $k$보다 크다는 것이 중요하지 그 숫자의 값은 중요하지 않습니다.
그럼 이 쿼리들을 $k$가 큰 순서로, $k$가 같으면 2번 쿼리가 먼저 나오도록 정렬해 봅시다. 세그먼트 트리를 하나 만든 다음 1번 쿼리고 오면 $i$에 1 추가, 2번 쿼리가 오면 $i$부터 $j$ 사이에 있는 1의 개수를 카운트를 하면 이 문제를 해결할 수 있습니다.
코드
더보기
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct data {
int q, x, y, z,i;
inline bool operator<(const data& temp)const {
return z > temp.z || z == temp.z && q < temp.q;
}
}b[300000];
int d[200000];
int it[400000], t = 1;
void up(int p) {
it[p]++;
while (p != 1) {
p >>= 1;
it[p]++;
}
}
int find(int s, int e, int i, int x, int y) {
if (x <= s && e <= y) return it[i];
if (y<s || x>e) return 0;
return find(s, (s + e) / 2, i * 2, x, y) + find((s + e) / 2 + 1, e, i * 2 + 1, x, y);
}
int main() {
int n, i,m;
scanf("%d", &n);
for (i = 1;i <= n;i++) {
scanf("%d", &b[i].z);
b[i].x = i;
b[i].q = 2;
}
while (t < n) t <<= 1;
scanf("%d", &m);
for (i = n+1;i <= n+m;i++) {
scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
b[i].q = 1;
b[i].i = i - n;
}
sort(b + 1, b + n + m + 1);
for (i = 1;i <= n + m;i++) {
if (b[i].q == 1) {
d[b[i].i] = find(1, t, 1, b[i].x, b[i].y);
}
else {
up(t - 1 + b[i].x);
}
}
for (i = 1;i <= m;i++) printf("%d\n", d[i]);
return 0;
}
'PS - 알고리즘' 카테고리의 다른 글
3043 - 장난감 탱크 (0) | 2024.05.31 |
---|---|
11873 - 최대 직사각형 (0) | 2024.05.26 |
1055 - 끝이없음 (0) | 2024.05.26 |
6549 - 히스토그램에서 가장 큰 직사각형 (0) | 2024.05.24 |
MCMF(min cut max flow) (0) | 2024.05.22 |