Home Clock Tree(18785, P5, c++)
Post
Cancel

Clock Tree(18785, P5, c++)

Clock Tree

Clock Tree

문제

(GPT 번역)
Farmer John의 새 헛간은 특이한 구조다.
방은 총 N개(2≤N≤2500)이고 1부터 N까지 번호가 매겨져 있다.
복도는 N−1개이며, 어떤 방에서든 복도를 따라 다른 모든 방으로 이동할 수 있다.
각 방에는 1~12가 적힌 원형 시계가 하나씩 있고 시계에는 시침은 항상 정수 눈금만 가리킨다.
소 Bessie는 모든 시계가 12를 가리키도록 맞추고 싶다.

Bessie는 헛간을 돌아다니면서 어떤 방에 ‘들어갈 때마다’ 그 방의 시계를 한 칸 앞으로 민다.
예를 들어 5를 가리키던 시계는 6이 되고, 12를 가리키던 시계는 1이 된다.
같은 방에 여러 번 들어가면 들어갈 때마다 한 칸씩 앞으로 간다.
Bessie가 어느 방에서 출발해야 모든 시계를 12로 맞출 가능성이 있는지를 알아내라.

출발한 방의 시계는 처음에는 움직이지 않는다.
하지만 그 방에 다시 들어오면 그때는 다른 방과 마찬가지로 한 칸 앞으로 간다.
시계는 Bessie가 방에 들어갈 때만 움직이며, 저절로는 움직이지 않는다.
또한 복도에 들어갔으면 반드시 반대쪽 끝으로 나와야 하며, 중간에서 되돌아올 수 없다.

입력

첫 줄에 N.
둘째 줄에 N개의 정수(각각 1~12): 각 방의 초기 시계 값.
다음 N−1줄에 복도 정보 a,b (1~N): 방 a와 b가 연결되어 있음.

출력

Bessie가 출발했을 때 모든 시계를 12로 맞출 수 있는 출발 방의 개수를 출력한다.

풀이

우선 문제 조건으로 보면 복도는 N-1개이고 다른 모든 방으로 이동할 수 있으므로 트리 구조이다.

관찰을 하다보면 붙어있는 것들의 합이 시작지점 기준 1또는 0이 차이난다는 것을 확인할 수 있다.
(붙어있다는 의미는 간선으로 이어졌다는 뜻)

그래서 조금 더 관찰을 해보니 이분 그래프로 접근하면 깔끔하게 풀리는 것 같다.
모든 트리는 이분 그래프이기 때문에 임의의 시작 지점을 놓은 뒤 DFS를 통해 짝수 depth와 홀수 depth로 나눈다.
시작 방은 처음엔 증가하지 않고, Bessie가 이동할 때마다 도착한 방의 색 집합 합만 +1씩 늘어난다.

그러면 짝수 인덱스들의 합과 홀수 인덱스들의 합을 비교해서 답을 구할 수 있게 된다.
S_even이 짝수 인덱스 방의 수의 합, S_odd가 홀수 인덱스 방의 수의 합이라고 하면

S_even(mod 12) == S_odd(mod 12)라면 어디서 시작하든 가능하고
S_even(mod 12) == S_odd + 1(mod 12)라면 짝수 방에서 시작할 때 가능하고
S_even + 1 (mod 12) == S_odd(mod 12)라면 홀수 방에서 시작할 때 가능하다.
위의 경우가 전부 아니라면 불가능하다.

아이디어를 떠올리는게 어려웠던 문제다.

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
#define ll long long
#define i128 __int128_t
#include <bits/stdc++.h>
using namespace std;
long long n, m, t, k = 0;
bool flag = 0;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
int main()
{
    ios_base ::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    long long mod = 998'244'353;
    using P=pair<int,int>;

    long long a, b, c, d;
    long long ans = 0;
    
    cin >> n;
    vector<int> v(n+1);
    for(int i=1;i<=n;i++)cin>>v[i];

    vector<vector<int>> graph(n+1);
    for(int i=0;i<n-1;i++){
        cin >> a >> b;
        graph[a].push_back(b);
        graph[b].push_back(a);
    }

    int t_odd=0;
    int t_even=0;
    int cnt_odd=0;
    int cnt_even=0;
    vector<bool> visit(n+1, 0);

    function <void(int, bool)> dfs=[&](int x, bool ok){
        visit[x]=1;
        if(!ok){
            t_odd+=v[x];
            cnt_odd++;
        }
        else{
            t_even+=v[x];
            cnt_even++;
        }

        for(int i=0;i<graph[x].size();i++){
            if(!visit[graph[x][i]])
                dfs(graph[x][i], !ok);
        }
    };
    dfs(1, 0);

    t_odd%=12;
    t_even%=12;

    if(t_odd==t_even)cout<<n;
    else if((t_odd+1)%12==t_even)cout<<cnt_even;
    else if((t_even+1)%12==t_odd)cout<<cnt_odd;
    else cout<<0;

    return 0;
}
This post is written by PRO.

하이퍼 토마토(17114, G1, c++)

10. 좌표압축