스크류바가 코딩하는 블로그
카테고리아카이브태그소개
자동

© 2026 스쿠루. All rights reserved.

개인정보 처리방침RSS
2026.08.06·1 min read·PS

Min of Restricted Sum

AtCoder Beginner Contest 396 E

AtCoder그래프 이론그래프 탐색동적 계획법

문제

링크: Min of Restricted Sum

난이도: 1379

풀이

정수 nnn의 bbb번째 비트를 n&2bn \& 2^bn&2b라 합시다.

그렇다면 주어진 조건 AXi⊕AYi=ZiA_{X_i} \oplus A_{Y_i} = Z_iAXi​​⊕AYi​​=Zi​를 (AXi&2b)⊕(AYi&2b)=Zi&2b(A_{X_i} \& 2^b) \oplus (A_{Y_i} \& 2^b) = Z_i \& 2^b(AXi​​&2b)⊕(AYi​​&2b)=Zi​&2b인 조건 31개로 나눌 수 있습니다.

서로 다른 위치의 비트는 xor 연산에서 서로에게 영향을 주지 않기 때문입니다.

이제 문제를 어떤 0≤b<310 \leq b < 310≤b<31에 대해 AiA_iAi​를 몇 개 골라 bbb번째 비트를 최소한으로 켜서 xor 결과가 Zi&2bZ_i \& 2^bZi​&2b와 일치하도록 만드는 문제를 각각 푸는 것으로 나눌 수 있습니다.

주어진 조건을 XiX_iXi​와 YiY_iYi​를 가중치 ZiZ_iZi​인 간선으로 잇는 것으로 생각합시다.

만들어진 그래프에서 어떤 연결 요소 G=(V,E)G = (V, E)G=(V,E)에서 임의의 정점 하나를 뽑아 rrr이라 합시다.

만약 ArA_rAr​의 bbb번째 비트가 결정된다면 같은 연결 요소에 속한 다른 모든 정점의 bbb번째 비트도 결정됩니다.

이는 트리 동적 계획법으로 O(∣V∣)O(|V|)O(∣V∣)에 구할 수 있습니다. ArA_rAr​의 bbb번째 비트가 000인 경우와 111인 경우를 각각 계산한 다음 더 작은 방법을 선택하면 됩니다.

이를 0≤b<310 \leq b < 310≤b<31에 대해 모두 수행하면 답을 구할 수 있습니다.

코드

#include <bits/stdc++.h>
using namespace std;
 
vector<pair<int, int>> adj[202020];
 
bool visited[202020];
 
int root[202020], sz[202020];
 
void preprocessGraph(int cur) {
    visited[cur] = true;
 
    int r = root[cur];
    sz[r]++;
 
    for (auto [child, _] : adj[cur]) {
        if (visited[child]) continue;
        root[child] = r;
        preprocessGraph(child);
    }
}
 
bool impossible = false;
bool isSet[202020];
int setCnt[202020];
 
void calcSetCnt(int b, int cur) {
    visited[cur] = true;
 
    setCnt[root[cur]] += isSet[cur];
 
    for (auto [child, z] : adj[cur]) {
        if (visited[child]) {
            if ((isSet[cur] ^ isSet[child]) != ((z >> b) & 1))
                impossible = true;
            continue;
        }
 
        isSet[child] = isSet[cur] ^ ((z >> b) & 1);
        calcSetCnt(b, child);
    }
}
 
int A[202020];
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int N, M;
    cin >> N >> M;
 
    for (int i = 1; i <= M; i++) {
        int X, Y, Z;
        cin >> X >> Y >> Z;
 
        adj[X].emplace_back(Y, Z);
        adj[Y].emplace_back(X, Z);
    }
 
    for (int v = 1; v <= N; v++) {
        if (visited[v]) continue;
        root[v] = v;
        preprocessGraph(v);
    }
 
    for (int b = 0; b < 31; b++) {
        fill(visited + 1, visited + N + 1, false);
        fill(isSet + 1, isSet + N + 1, false);
        fill(setCnt + 1, setCnt + N + 1, 0);
 
        for (int v = 1; v <= N; v++) {
            if (not visited[v]) {
                calcSetCnt(b, v);
 
                if (impossible) {
                    cout << -1;
                    return 0;
                }
            }
 
            int r = root[v];
            bool invert = setCnt[r] > sz[r] - setCnt[r];
 
            A[v] |= (isSet[v] ^ invert) << b;
        }
    }
 
    for (int i = 1; i <= N; i++) cout << A[i] << ' ';
 
    return 0;
}

목차

  • 문제
  • 풀이
  • 코드

댓글

이름과 이메일을 입력해 댓글을 남겨주세요.