문제 링크 https://www.acmicpc.net/problem/2243
문제 소개
두가지 행동을 하는 문제이다.
1. x 맛의 사탕의 갯수를 k개 변화시킨다.
2. k번째로 맛있는 사탕의 맛 수치를 출력한다. (1이 가장 맛있고 1000000이 최악)
문제 풀이
1번 쿼리는 굉장히 자주 나오는 쿼리이고 하나도 까다롭지 않게 작용한다. 중요한 것은 2번쿼리로 그니까 1부터 순서대로 늘어놓았을 때 k번째 숫자를 구하고 싶은 것이다.
구간 합 구하기의 가장 간단한 세그를 생각해보면 우리가 얻은 수 있는 값은 구간 의 합이다. 이 문제는 세그에는 추가적인 옵션을 가하지 않고, 이 값을 이용하여 문제를 해결할 수 있다.
이분 탐색이다. 1~i 구간의 합을 구할 수 있다. 그 값이 k이하인지 초과인지에 따라서 k번째 숫자가 i이하인지 초과인지 구할 수 있다. 따라서 lgM (M은 맛 수치의 범위)번의 질문을 통해서 k번째 숫자를 구할 수 있고, 한번의 질문에 답을 하는데 걸리는 시간 복잡도는 $O(lgM)$이므로 총 시간 복잡도 $O(Mlg^2M)$의 시간복잡도에 전체 문제를 해결할 수 있다. 세그에 이분탐색을 하는 테크닉은 각각의 테크닉이 활용도가 높아 전체 활용도도 굉장히 높다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll tree[4040404];
void upt(ll idx,ll val,ll s,ll e,ll nod){
if(idx<s||idx>e) return ;
tree[nod]+=val;
if(s==e) return;
upt(idx,val,s,(s+e)/2,nod*2);
upt(idx,val,(s+e)/2+1,e,nod*2+1);
}
ll val(ll l,ll r,ll s,ll e,ll nod){
if(r<s||e<l) return 0;
if(l<=s&&e<=r) return tree[nod];
return val(l,r,s,(s+e)/2,nod*2)+val(l,r,(s+e)/2+1,e,nod*2+1);
}
ll sol(ll ans){
ll lo=0,hi=1000001;
while(lo<hi){
ll mid=(lo+hi)/2;
if(val(1,mid,1,1000000,1)<ans) lo=mid+1;
else hi=mid;
//printf("%lld %lld %lld %lld\n",lo,mid,hi,ans);
}
return lo;
}
int main(){
ll i,j,k,l,m,n;
scanf("%lld",&n);
while(n--){
ll a,b,c;
scanf("%lld",&a);
if(a==1){
scanf("%lld",&b);
ll ans=sol(b);
printf("%lld\n",ans);
upt(ans,-1,1,1000000,1);
}
else{
scanf("%lld %lld",&b,&c);
upt(b,c,1,1000000,1);
}
}
}
|
'코딩 > 백준 문제 풀이' 카테고리의 다른 글
BOJ 13554 - 수열과 쿼리 9 (0) | 2020.05.26 |
---|---|
BOJ 14435 - 놀이기구 2 (0) | 2020.05.24 |
BOJ 17411 - 가장 긴 증가하는 부분 수열 6 (3) | 2020.05.22 |
BOJ 17408 - 수열과 쿼리 24 (0) | 2020.05.22 |
BOJ 2463 - 비용 (0) | 2020.05.20 |