안녕하세요. 오늘은 물탱크에 물을 채울 거예요.
https://www.acmicpc.net/problem/18227
이 문제에서의 핵심 관찰이 있습니다.
바로 한 도시는 한번 물을 받을 때 일정한 양을 받는다는 것입니다.
그래서 한 도시에 있는 물의 양은 (그 도시가 한번에 받는 물의 양)x(물을 받은 횟수)로 정의할 수 있습니다.
그 도시가 한번에 받는 물의 양은 그 도시의 깊이 (depth)가 됩니다. 이는 DFS로 쉽게 구할 수 있습니다.
물을 받은 횟수는 그 노드의 자식들(자신을 포함한)이 물을 받은 횟수와 동일해집니다. 그래서 이를 빠르게 구해야합니다. 그래서 쓰이는 두 알고리즘이 바로 오일러 경로 테크닉과 세그먼트 트리입니다. 이는 회사문화 3문제와 굉장히 유사합니다.
#include <iostream>
#include <vector>
#define ll long long
using namespace std;
ll cnt = 0, EulerNum[202020] = { 0 }, depth[202020] = { 0 }, Start[202020] = { 0 }, End[202020] = { 0 };
vector <ll> v[202020];
void DFS(ll node, ll up)
{
depth[node] = depth[up] + 1;
EulerNum[node] = ++cnt;
Start[node] = cnt;
for (ll next : v[node])
if (next != up)
DFS(next, node);
End[node] = cnt;
}
ll tree[808080] = { 0 };
ll SUM(ll s, ll e, ll node, ll l, ll r)
{
if (e < l || r < s) return 0;
if (l <= s && e <= r) return tree[node];
ll mid = (s + e) / 2;
return SUM(s, mid, node * 2, l, r) + SUM(mid + 1, e, node * 2 + 1, l, r);
}
void change(ll s, ll e, ll node, ll idx) //idx번지에 1 추가
{
if (e < idx || idx < s) return;
tree[node]++;
if (s == e) return;
ll mid = (s + e) / 2;
change(s, mid, node * 2, idx);
change(mid + 1, e, node * 2 + 1, idx);
}
int main(void)
{
ios_base::sync_with_stdio(false); cin.tie(NULL);
ll N, C, i, a, b, M;
cin >> N >> C;
for (i = 0; i < N - 1; i++)
{
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
DFS(C, 0);
cin >> M;
for (i = 0; i < M; i++)
{
cin >> a >> b;
if (a == 1)
change(1, N, 1, EulerNum[b]);
else
cout << SUM(1, N, 1, Start[b], End[b]) * depth[b] << "\n";
}
}
감사합니다.