• 저번 문제에서 n, k의 범위가 10^5로 넓어진 문제이다. (풀고 걱정했던 내용이 바로 나왔다.)

문제 풀이

  • segment tree의 leaf노드를 n명의 사람이라 하고 제거되지 않으면 1, 제거된 상태를 0이라 정의했다. 이후 저번 문제와 똑같은 방법으로 풀었다.

  • [0, e]의 구간합이 k인 e를 구할 때 처음에는 parametric search를 이용하려했는데 7469번을 풀 때 segment tree 탐색하던 것이 생각나 그걸 사용했다. 저 구간을 구해도 끝에 0이 몇 개 붙는 경우가 존재하기 때문에 findlast1을 이용해서 저 구간의 leaf노드 중 값이 1인 마지막 leaf를 찾았다.

소스코드
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<cstdio>
#include<vector>
#include<tuple>
using namespace std;
typedef vector<int> vi;
typedef tuple<int, int, int> ti;

int mid(int s, int e){return (s+e)>>1;}
int init(vi& num, vi& st, int tidx, int s, int e);
int query1(vi& st, int tidx, int s, int e, int fl, int fr);//return partial sum
ti query2(vi& st, int tidx, int s, int e, int val);//return index of e, sum(0, e)==val
int findlast1(vi& st, int n, int tidx, int s, int e);
void update(vi& st, int tidx, int s, int e, int nidx, int diff);

int main()
{
	int n, k, curk, curp, sz=1;
	scanf("%d %d", &n, &k);
	for(;sz<n;sz*=2);
	vi st(2*sz), num(n, 1), sol;
	init(num, st, 1, 0, n-1);
	curp=-1;
	for(int i=n;i>0;i--){
		int idx, s, e, aftercurp, curk;
		if(curp==n-1) aftercurp=0;
		else aftercurp=query1(st, 1, 0, n-1, curp+1, n-1);
		if(aftercurp>=k) curk=(k+i-aftercurp)%i;
		else curk=(k-aftercurp)%i;
		if(curk==0) curk=i;
		tie(idx, s, e)=query2(st, 1, 0, n-1, curk);
		curp=findlast1(st, n, idx, s, e);
		sol.push_back(curp+1);
		update(st, 1, 0, n-1, curp, -1);
	}
	printf("<");
	for(int i=0;i<sol.size()-1;i++) printf("%d, ", sol[i]);
	printf("%d>", sol.back());
}

int init(vi& num, vi& st, int tidx, int s, int e){
	if(s==e) return st[tidx]=num[s];
	return st[tidx]=init(num, st, tidx*2, s, mid(s, e))+init(num, st, tidx*2+1, mid(s, e)+1, e);
}

int query1(vi& st, int tidx, int s, int e, int fl, int fr){
	if(s>fr || e<fl) return 0;
	if(s>=fl && e<=fr) return st[tidx];
	return query1(st, tidx*2, s, mid(s, e), fl, fr)+query1(st, tidx*2+1, mid(s, e)+1, e, fl, fr);
}

ti query2(vi& st, int tidx, int s, int e, int val){
	if(st[tidx]==val) return make_tuple(tidx, s, e);
	if(val>st[tidx*2]) return query2(st, tidx*2+1, mid(s, e)+1, e, val-st[tidx*2]);
	return query2(st, tidx*2, s, mid(s, e), val);
}

int findlast1(vi& st, int n, int tidx, int s, int e){
	if(s==e) return s;
	if(st[tidx*2+1]==0) return findlast1(st, n, tidx*2, s, mid(s, e));
	return findlast1(st, n, tidx*2+1, mid(s, e)+1, e);
}

void update(vi& st, int tidx, int s, int e, int nidx, int diff){
	if(s>nidx || e<nidx) return;
	st[tidx]+=diff;
	if(s!=e){
		update(st, tidx*2, s, mid(s, e), nidx, diff);
		update(st, tidx*2+1, mid(s, e)+1, e, nidx, diff);
	}
}

풀고나서

  • 처음에는 segment tree의 쿼리가 O(logN)이기 때문에 O(N^2logN)으로 안될 것 같았는데 구간합이 curk인 구간 탐색을 logN만에 구현해서 O(NlogN)으로 풀었다. 위에 7469번에서 이 내용도 구현했었는데 까먹었다… 저번 문제를 풀고 나서 고민했던 STL과 비슷한 게 segment tree인 것 같다.

  • tuple을 한 번 사용해보았다.

  • 아무리 생각해도 실버3난이도의 문제는 아닌거가튼뒈


(2020.11.12)

  • 내 질문 떄문에 시간제한이 바뀌었고 그 결과 난이도도 다시 책정되었다. 이제 플레티넘 5 난이도의 문제로 바뀌었다!!

Leave a comment