※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※
이번에 볼 문제는 백준 15957번 문제인 음악 추천이다.
문제는 아래 링크를 확인하자.
https://www.acmicpc.net/problem/15957
15957번: 음악 추천
입력의 첫째 줄에는 세 정수로, 곡의 수 N(2 ≤ N ≤ 100,000), 추천 알고리즘의 결과 데이터의 수 K(1 ≤ K ≤ 100,000), 목표 점수 J(10 ≤ J ≤ 108)가 주어진다. 각각의 곡은 1번부터 N번까지 번호가 붙어
www.acmicpc.net
주어지는 음악들은 트리구조를 가지고 있으므로, 트리의 각 노드에 dfs순회순 인덱스를 새로 붙여(euler tour technique) 각 노드에 대해 그 노드의 자식들이 자신의 오른쪽에 항상 모여있게 인덱스를 새로 정할 수 있다.
위 새로 정한 인덱스를 이용해 lazy propagation을 지원하는 세그먼트트리를 만들면 시간순에 따라 노래에 점수를 부여하는 시뮬레이션을 빠르게 시행할 수 있다.
각 가수의 목표 점수 달성 시간의 범위를 0 이상 1000000007이하로 잡고, 위의 시뮬레이션을 한번 진행할 때마다 모든 가수의 목표 달성 순간이 들어있을 범위를 각각 절반으로 줄여나가는 이분탐색으로 문제를 해결하자.
아래는 제출한 소스코드이다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
int N, K; ll J;
ll lazy[262145];
void upd(int L, int R, int qL, int qR, int qVal, int sI) {
if (R < qL || qR < L) return;
if (qL <= L && R <= qR) lazy[sI] += qVal;
else upd(L, (L + R) / 2, qL, qR, qVal, sI * 2), upd((L + R) / 2 + 1, R, qL, qR, qVal, sI * 2 + 1);
}
ll pointval(int qI) {
int L = 1, R = 100000, sI = 1;
while (L < R) {
if (lazy[sI]) {
lazy[sI * 2] += lazy[sI];
lazy[sI * 2 + 1] += lazy[sI];
lazy[sI] = 0;
}
int mid = (L + R) / 2;
if (qI > mid) L = mid + 1, sI = sI * 2 + 1;
else R = mid, sI = sI * 2;
}
return lazy[sI];
}
vector<int> G[100001];
int idx = 1;
int dfi[100001];
int childcnt[100001];
int dfs(int cur) {
dfi[cur] = idx++;
int ret = 0;
for (auto& node : G[cur]) {
ret += dfs(node);
}
childcnt[cur] = ret++;
return ret;
}
vector<int> singer[100000]; // [i]: i번 가수가 부른 곡들의 num
struct query {
int idx; // singernum
int low = 0, mid = 500000003, high = 1000000007;
};
bool qcomp(query& q1, query& q2) {
return q1.mid < q2.mid;
}
query queries[100000];
struct datum {
int T;
int qL, qR, qVal;
datum() {};
datum(int T, int qL, int qR, int qVal) {
this->T = T, this->qL = qL, this->qR = qR, this->qVal = qVal;
}
};
bool dcomp(datum& d1, datum& d2) {
return d1.T < d2.T;
}
datum DATA[100000];
void PBS() {
for (auto& x : lazy) x = 0;
sort(queries, queries + N, qcomp);
int kI = 0, qI = 0;
while (kI < K && qI < N) {
if (DATA[kI].T > queries[qI].mid) {
if (queries[qI].high == queries[qI].low) {
qI++;
continue;
}
ll total = 0;
for (auto x: singer[queries[qI].idx]) {
total = min(total + pointval(dfi[x]), 1000000000000000007LL);
}
if (total > J * singer[queries[qI].idx].size()) queries[qI].high = queries[qI].mid;
else queries[qI].low = queries[qI].mid + 1;
queries[qI].mid = (queries[qI].high + queries[qI].low) / 2;
qI++;
}
else {
upd(1, 100000, DATA[kI].qL, DATA[kI].qR, DATA[kI].qVal, 1);
kI++;
}
}
while (qI < N) {
if (queries[qI].high == queries[qI].low) {
qI++;
continue;
}
ll total = 0;
for (auto x : singer[queries[qI].idx]) {
x = dfi[x];
total = min(total + pointval(x), 1000000000000000007LL);
}
if (total > J * singer[queries[qI].idx].size()) queries[qI].high = queries[qI].mid;
else queries[qI].low = queries[qI].mid + 1;
queries[qI].mid = (queries[qI].high + queries[qI].low) / 2;
qI++;
}
}
int ans[100001];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> N >> K >> J;
for (int i = 2; i <= N; i++) {
int x; cin >> x;
G[x].emplace_back(i);
}
dfs(1);
for (int i = 1; i <= N; i++) {
int x; cin >> x; x--;
singer[x].emplace_back(i);
}
for (int i = 0; i < N; i++) queries[i].idx = i;
for (int k = 0; k < K; k++) {
int T, P, S; cin >> T >> P >> S;
DATA[k] = datum(T, dfi[P], dfi[P] + childcnt[P], S / (childcnt[P] + 1));
}
sort(DATA, DATA + K, dcomp);
for (int i = 0; i < 31; i++) {
PBS();
}
for (int i = 0; i < N; i++) {
query& q = queries[i];
for (auto& song : singer[q.idx]) {
ans[song] = q.low;
}
}
for (int i = 1; i <= N; i++) {
if (ans[i] == 1000000007) cout << -1 << '\n';
else cout << ans[i] << '\n';
}
}
728x90
'BOJ' 카테고리의 다른 글
[BOJ 13519 // C++] 트리와 쿼리 10 (0) | 2022.07.28 |
---|---|
[BOJ 13557 // C++] 수열과 쿼리 10 (0) | 2022.07.27 |
[BOJ 8217 // C++] 유성 (0) | 2022.07.25 |
[BOJ 15811 // C++] 복면산?! (0) | 2022.07.24 |
[BOJ 12355 // C++] Ocean View (Large) (0) | 2022.07.24 |