티스토리 뷰

COI 2022 (크로아티아) 를 돌아 네 문제 중 세 문제를 풀어 334점을 받았다. 400점을 받아야 했을 셋인 것 같은데 아쉽다.

 

[BOJ 25447] D. Vingete (00:18)

화성 지도 세그를 알면 매우 쉽게 맞을 수 있다. 사실상 이 세그먼트 트리를 아는지 물어보는 문제기 때문에 더 이상 설명하지 않는다.

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

struct Edge{
    int s, e, l, r;
    Edge(){}
    Edge(int s, int e, int l, int r): s(s), e(e), l(l), r(r){}
};

struct segTree{
    int cnt[2000002], sz[2000002], sum[2000002];

    void init(int i, int l, int r, int *arr){
        cnt[i] = sum[i] = 0;
        if(l==r){
            sz[i] = arr[l];
            return;
        }
        int m = (l+r)>>1;
        init(i*2, l, m, arr);
        init(i*2+1, m+1, r, arr);
        sz[i] = sz[i*2] + sz[i*2+1];
    }

    void update(int i, int l, int r, int s, int e, int v){
        if(r<s || e<l) return;
        if(s<=l && r<=e){
            cnt[i] -= v;
            sum[i] = (cnt[i] ? sz[i] : sum[i*2] + sum[i*2+1]);
            return;
        }
        int m = (l+r)>>1;
        update(i*2, l, m, s, e, v);
        update(i*2+1, m+1, r, s, e, v);
        sum[i] = (cnt[i] ? sz[i] : sum[i*2] + sum[i*2+1]);
    }

    int query(){
        return sum[1];
    }
} tree;

int n, m, k;
int arr[300002];
vector<Edge> link[100002];
vector<int> stp;

int ans[100002];

void dfs(int x, int p){
    ans[x] = tree.query();
    for(Edge e: link[x]){
        if(e.e == p) continue;
        tree.update(1, 1, k, e.l, e.r, 1);
        dfs(e.e, x);
        tree.update(1, 1, k, e.l, e.r, -1);
    }
}

int main(){
    scanf("%d %d", &n, &m);
    stp.push_back(1), stp.push_back(m+1);
    for(int i=1; i<n; i++){
        int x, y, l, r;
        scanf("%d %d %d %d", &x, &y, &l, &r);
        stp.push_back(l), stp.push_back(r+1);
        link[x].push_back(Edge(x, y, l, r));
        link[y].push_back(Edge(y, x, l, r));
    }
    sort(stp.begin(), stp.end());
    stp.erase(unique(stp.begin(), stp.end()), stp.end());
    k = (int)stp.size() - 1;
    for(int i=1; i<=k; i++) arr[i] = stp[i] - stp[i-1];
    for(int i=1; i<=n; i++){
        for(Edge &edge: link[i]){
            edge.l = lower_bound(stp.begin(), stp.end(), edge.l) - stp.begin() + 1;
            edge.r = lower_bound(stp.begin(), stp.end(), edge.r + 1) - stp.begin();
        }
    }
    tree.init(1, 1, k, arr);

    dfs(1, -1);

    for(int i=2; i<=n; i++) printf("%d\n", ans[i]);
}

 

BOJ 25445. Mensza (2:14)

(아직 BOJ에 업로드가 안 되었다. 언젠간 올라오지 않을까)

 

Three-step 문제이다. 두 수 A, B ($A \neq B$, $A, B \le N = 5 \times 10^5$) 가 있다. 안나는 $A$를 보고 길이 $L$ 이하의 배열을 만든다. 브루노는 $B$를 보고 길이 $L$ 이하의 또 다른 배열을 만든다. $L=20$이다. 주최자는 두 배열을 모은 뒤, 각 수가 등장한 횟수만을 뽑고 셔플해 크리스에게 전달한다. (예를 들어, 두 배열이 $[1, 2, 3]$과 $[1, 1, 3, 4]$면 크리스는 $[1, 1, 2, 3]$을 받을 수 있다.) 크리스는 이 배열을 보고 $A$와 $B$ 중 무엇이 더 큰지 맞혀야 한다.

 

Subtask 2까지는 제한이 조금 더 여유롭고, 다른 방식으로 풀린다. Full Task 역시 어렵지 않다. 관건은, $A<B$면 두 배열에 겹치는 원소가 있게 하고, $A>B$면 그런 원소가 없게 하자는 아이디어이다. $B$에 해당하는 배열은 수 $B$를 이진수로 나타냈을 때 앞에 있는 비트부터 취한 접두사들로만 구성한다. 예를 들어, $B=19=1011_{(2)}$면, 배열은 $[16, 18, 19]$를 리턴한다. 이제 $A$에 대한 배열을 어떻게 만들어야 할까? $A<B$일 경우, $A$와 $B$가 처음으로 달라지는 비트들이 있다. 이들 비트로 가능한 지점에 대응되는 값을 모두 넣어준다. 예를 들어, $A=17=1001_{(2)}$면, $1010$, $1100$, $10000$, $100000$, $\cdots$ 등을 넣어준다. 각각 $2^1, 2^2, 2^4, 2^5, \cdots$ 비트에서 처음으로 B가 A보다 커질 때 B의 배열에 등장할 수 있는 배열들이다.

 

이렇게 구현하면 문제를 맞을 수 있다.

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int L, st;
int q;
char str[15];

int main(){
    scanf("%d", &L);
    st = (L==200 ? 1 : L==110 ? 2 : 3);
    scanf("%d", &q);
    while(q--){
        scanf("%s", str);
        if(str[0] == 'a'){
            int x;
            scanf("%d", &x);
            vector<int> v;
            for(int i=0; i<19; i++){
                if((x>>i)&1) continue;
                v.push_back((x+(1<<i)) >> i << i);
            }
            printf("%d ", (int)v.size()); for(auto x: v) printf("%d ", x); puts(""); fflush(stdout);
        }
        else if(str[0] == 'b'){
            int x;
            scanf("%d", &x);
            vector<int> v;
            int T = 0;
            for(int i=18; i>=0; i--) if((x>>i)&1) T+=(1<<i), v.push_back(T);
            printf("%d ", (int)v.size()); for(auto x: v) printf("%d ", x); puts(""); fflush(stdout);
        }
        else{
            int len;
            scanf("%d", &len);
            vector<int> v(len);
            for(int i=0; i<len; i++){
                scanf("%d", &v[i]);
            }
            printf("%c\n", *max_element(v.begin(), v.end()) == 2 ?'B':'A');
        }
    }
}

 

BOJ 25444. Mađioničar (2:47)

이 문제도 거의 Manacher 알고리즘을 아는지 물어보는 문제다. 그런데 인터랙티브 방식이라 구현할 때 $2N$ 이하의 횟수로 문제를 풀기 위해서 조금 신경써 줘야 할 부분들이 많다. 이 문제도 더 이상 설명할 게 없다.

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int n;
int arr[200002];
int ans;

bool query(int l, int r){
    assert(l%2 == r%2);
    if(l%2==0) return true;

    printf("? %d %d\n", (l+1)/2, (r+1)/2);
    fflush(stdout);
    int x;
    scanf("%d", &x);
    return x;
}

int main(){
    scanf("%d", &n);
    n = 2*n-1;
    int maxV = 0, maxMid = 0;
    for(int i=1; i<=n; i++){
        if(maxV < i){ /// 바깥쪽이라 알 수 없는 경우
            for(int l=1; i+l<=n+1 && i-l>=0; l++){
                if(!query(i-l, i+l)) break;
                arr[i] = l;
            }
        }
        else{ /// 안쪽인 경우
            int m = maxMid, mlen = arr[m];
            int l = maxMid-mlen, r = maxMid+mlen;
            int q = i, p = m+m-q, plen = arr[p];
            if(p-plen > l) arr[q] = arr[p];
            else if(p-plen < l) arr[q] = r-q;
            else{
                arr[q] = p-l;
                for(int l=arr[q]+1; i+l<=n+1 && i-l>=0; l++){
                    if(!query(i-l, i+l)) break;
                    arr[q] = l;
                }
            }
        }
        if(maxV <= i+arr[i]) maxV = i+arr[i], maxMid = i;
        ans = max(ans, arr[i]);
    }

    printf("! %d", ans);
}

 

BOJ 25446. Povjerenstvo (34점)

Subtask 1, 2는 크게 어렵지 않다. 1은 나가는 edge가 없는 것부터 위상 정렬 하듯이 봐주면서, 어떤 정점 $x$에 대해 $x \rightarrow y$의 간선이 있고 $y$가 이미 색칠된(commitee에 포함된) $y$가 있는 경우 색칠하지 않고, 그런 $y$가 없는 경우 색칠하면 된다. 2는 각 연결 성분에 대해 체스판 색칠을 해 주면 된다.

 

34점 이후부터는 나름 할만하다고 생각됐는데, 방향 그래프에서 SCC가 아닌 BCC로 접근하고 코딩하는 바람에 폭망했다.

 

Subtask 3부터는 SCC에 대한 관찰을 해야 한다. 모든 노드가 하나의 SCC이고, 노드가 한 개 이상이라고 가정해 보자. 홀수 사이클이 없으므로, 그냥 인접한 정점을 서로 다른 색으로 색칠하면 문제 조건을 만족할 것이다.

 

(작성중)

'코딩 > 국대 멘토링 교육' 카테고리의 다른 글

2023 5주차 멘토링 일지  (0) 2023.05.28
2023 3주차 멘토링 일지  (2) 2023.05.21
2023 2주차 멘토링 일지  (0) 2023.05.14
2023 1주차 멘토링 일지  (1) 2023.05.13
2021 국대 멘토링 일지 - 3주차  (0) 2021.04.24
공지사항
최근에 올라온 글
Total
Today
Yesterday