2019. 8. 29. 23:48ㆍ알고리즘 문풀/Others
10076번: 휴가
문제 지안지아는 타이완에서의 휴가를 계획하고 있다. 휴가동안 지안지아는 도시에서 도시로 이동하고 도시 안의 관광지들을 방문할 것이다. 타이완에는 하나의 고속도로를 따라서 개의 도시들이 위치한다. 이 도시들은 순서대로 0부터 n-1까지의 번호가 붙어있다. 임의의 i(0 < i < n-1)에 대해서, 도시 i의 인접한 도시는 도시 i-1과 i+1이다. 도시 0과 인접한 도시는 도시 1뿐이고, 도시 n-1과 인접한 도시는 도시 n-2뿐이다. 각 도시에는 여러
www.acmicpc.net
PS-hell 스터디에서 가장 먼저 해결한 문제다.
문제 내용
길이 의 선형 배열 가 있다.
번째 entry에서 시작해서 한 턴에 다음의 동작들을 할 수 있다.
초기 점수가 0점이고 현재 위치가 번 이라고 할 때,
- Stay : 현재 점수에 를 더한다. 단, 한 번 Stay한 곳의 배열 값을 중복해서 더할 수 없다.
- goLeft, goRight : 각각 번, 번 entry로 이동한다.
총 턴 동안 점수를 최대화하는 문제이다.
스포방지선
기본 관찰
Stay를 제외하면, 이동을 LL....LRR...R 또는 RR...RLL...L 형태로 하는 것이 최적임을 알 수 있다.
RR...RLL...L 형태는 배열을 뒤집어서 동일한 과정을 반복하는 것으로 고려할 수 있다.
따라서 탐색할 구간 을 고정하면 이동에 소모하는 턴(날짜) 수는 턴이므로 총 턴 동안 Stay를 할 수 있다.
따라서 에서 최대의 개 원소를 뽑아주면 된다. 을 고정하고 을 늘려가면서 std::set 등으로 관리하면 에 문제를 해결할 수 있다. 이 성립해야 한다.
DnC approach
시간복잡도를 줄이기 위해서는 모든 을 다 보면 안된다. 한 가지 중요한 관찰은 고정된 에 대해서 답을 최대로 하는 값을 이라고 하면, 이 에 따라 단조증가한다는 것이다.
따라서 다음의 pseudo-code가 잘 작동한다:
def solve(s, e, d, u): #[s, e] : candidates for l, [d, u] : candidates for r; i.e. f(m)
if(s > e): return
m = (s + e) // 2
opt = f(m, d, u) #gets f(m) with an appropriate O(e-s + u-d) procedure
solve(s, m-1, d, opt)
solve(m+1, e, opt, u)
가능한 후보를 Segment Tree나 set 등의 자료구조로 잘 manage하면 을 에 구할 수 있고, 전체 복잡도는 . 단, 분할 정복 과정에서 update가 번 정도만 수행되도록 잘 유지해야 한다. 구현 자유도는 굉장히 높은 것 같다. 메모리가 64MiB라서 Persistent Segment Tree를 쓰려면 주의해야 하는...줄 알았지만 PST로 만점을 받은 사람도 많이 있는 것 같다.
나는 가장 큰 개의 합이 지원되는 Segment Tree로 풀었다.
#include <bits/stdc++.h> | |
using namespace std; | |
using ll = long long; | |
int a[100005]; | |
int n, st, d; | |
vector<int> cmp; | |
struct Segtree{ | |
ll seg[262500]; | |
int cnt[262500]; | |
void init(){ | |
memset(seg, 0, sizeof(seg)); | |
memset(cnt, 0, sizeof(cnt)); | |
} | |
void upd(int now, int s, int e, int i, int v){ | |
if(i < s || i > e) return; | |
cnt[now] += v; | |
seg[now] += v * cmp[i]; | |
if(s == e) return; | |
int m = s + e >> 1; | |
upd(now << 1, s, m, i, v); | |
upd(now << 1 | 1, m+1, e, i, v); | |
} | |
ll sum_max_kth(int now, int s, int e, int k){ | |
if(!k) return 0; | |
if(s == e) return ll(min(cnt[now], k)) * cmp[s]; | |
int m = s + e >> 1; | |
if(cnt[now << 1 | 1] > k) return sum_max_kth(now<<1|1, m+1, e, k); | |
else return sum_max_kth(now << 1, s, m, k - cnt[now<<1|1]) + seg[now<<1|1]; | |
} | |
} S; | |
map<int, int> M; | |
bool chk[100005]; | |
void upd(int i, int v){ | |
if((v == 1) == chk[i]) return; | |
chk[i] ^= 1; | |
S.upd(1, 0, (int)cmp.size() - 1, a[i], v); | |
} | |
ll qry(int k){ | |
ll qv = S.sum_max_kth(1, 0, (int)cmp.size() - 1, k); | |
return qv; | |
} | |
void init(){ | |
cin >> n >> st >> d; | |
for(int i = 0; i < n; i++){ | |
cin >> a[i]; | |
cmp.push_back(a[i]); | |
} | |
sort(cmp.begin(), cmp.end()); | |
cmp.erase( unique(cmp.begin(),cmp.end()), cmp.end() ); | |
for(int i = 0; i < n; i++){ | |
a[i] = lower_bound(cmp.begin(), cmp.end(), a[i]) - cmp.begin(); | |
} | |
} | |
ll ans; | |
void dnc(int s, int e, int kmin, int kmax){ | |
if(s > e) return; | |
int m = s + e >> 1; | |
for(int i = m; i <= e; i++) upd(i, 1); | |
int klim = min(kmax, 2*m + d - st); | |
ll lans = 0; | |
int lansi = kmin; | |
for(int i = kmin; i <= klim; i++){ | |
upd(i, 1); | |
ll tmp = qry(d - (st-m) - (i-m)); | |
if(tmp > lans){ | |
lans = tmp; | |
lansi = i; | |
} | |
} | |
ans = max(ans, lans); | |
for(int i = klim; i >= lansi; i--) upd(i, -1); | |
if(m < e){ | |
int mm = (m+1+e) >> 1; | |
for(int i = m; i <= mm; i++) upd(i, -1); | |
dnc(m+1, e, lansi, kmax); | |
} | |
for(int i = kmax; i >= kmin; i--) upd(i, -1); | |
if(s < m){ | |
int ss = (s+m-1) >> 1; | |
for(int i = m; i < min(kmin, e+1); i++) upd(i, 1); | |
dnc(s, m-1, kmin, lansi); | |
for(int i = ss; i <= e; i++) upd(i, -1); | |
} | |
for(int i = m; i <= e; i++) upd(i, -1); | |
for(int i = kmin; i <= lansi; i++) upd(i, -1); | |
} | |
void pro(){ | |
S.init(); | |
memset(chk,0,sizeof(chk)); | |
int l = max(0, st - d); | |
dnc(l, st, st, n-1); | |
} | |
int main(){ | |
ios_base::sync_with_stdio(0); cin.tie(0); | |
init(); | |
pro(); | |
reverse(a, a+n); st = n-1-st; | |
pro(); | |
cout << ans << '\n'; | |
} |
sketch
을 에서 가장 큰 개 수의 합이라고 하자.
그렇다면 에 대해 가 성립한다. 이를 total monotonicity라고 한다.
Totally monotone matrix 에 대해, row minima를 구하는 알고리즘이 알려져 있다. 을 amortized 정도에 구할 수 있으니, 아마 에 문제를 풀 수 있을 것이다. 솔직히 구현이 귀찮아서 해보진 않았다.
'알고리즘 문풀 > Others' 카테고리의 다른 글
UCPC 2020 본선 후기 (0) | 2020.08.01 |
---|---|
ICPC Seoul Regional 2019 후기 (1) | 2019.11.13 |
[PS 켠왕 #1] BOJ 10641 The J-th Number (0) | 2019.08.15 |
NYPC 2018 넋두리 (3) | 2018.10.28 |
[추석맞이 폴란드 스터디] 180925 (0) | 2018.09.26 |