https://codeforces.com/contest/1644

 

Dashboard - Educational Codeforces Round 123 (Rated for Div. 2) - Codeforces

 

codeforces.com

 

A. Doors and Keys

더보기

풀이

  • 각 키와 그에 맞는 도어에 대해 키 -> 도어 순서로 되어있지 않으면 NO

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        string s;
        cin >> s;
        set<int> key;

        bool answer = true;
        for (auto e: s) {
            if ('a' <= e && e <= 'z') {
                key.insert(e);
            } else {
                if (!key.count(e - 'A' + 'a')) {
                    answer = false;
                    break;
                }
            }
        }

        if (answer) {
            cout << "YES\n";
        } else {
            cout << "NO\n";
        }
    }
}

 

B. Anti-Fibonacci Permutation

더보기

풀이

  • 피보나치 수열이 증가수열이므로 내림차순으로 순열을 정렬하면 뭔가 되지 않을까부터 생각
  • 맨 뒤의 1을 앞으로 하나씩 옮기면 된다.
  • $[N, N - 1, ... 2, 1], [N, N - 1, ..., 1, 2], ..., [1, N, N - 1, ..., 2]$

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;

        vector<int> a(N - 1);
        for (int i = 0; i < N - 1; ++i) {
            a[i] = i + 2;
        }

        sort(rall(a));
        for (int i = 0; i < N; ++i) {
            bool did = false;
            for (int j = 0; j < N; ++j) {
                if (!did && i == j) {
                    cout << "1 ";
                    did = true;
                } else {
                    cout << a[j - did] << ' ';
                }
            }
            cout << '\n';
        }

    }
}

 

C. Increase Subarray Sums

더보기

풀이

  • 길이 1, 2, ..., N 부분수열에 대해서 길이에 대한 maxSum 값을 구해둔다
  • 그 maxSum 부분 수열의 원소들에 최대한 X 를 더하는게 이득
  • 곱하는 위치수 k 에 대한 길이 d 부분수열의 합의 최댓값은 $maxSum[d] + min(d, k), * X$  

코드

  • 시간복잡도: $O(N^2)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N, X;
        cin >> N >> X;
        vector<long long> a(N);
        vector<long long> pa(N, 0);
        for (int i = 0; i < N; ++i) {
            cin >> a[i];
            pa[i] = a[i];
            if (i) pa[i] += pa[i - 1];
        }

        vector<long long> maxSum(N + 1, -1e18);

        maxSum[0] = 0;
        for (int d = 1; d <= N; ++d) {
            long long maxSum_ = -1e18;
            for (int i = 0; i + d - 1 < N; ++i) {
                long long sum = pa[i + d - 1];
                if (i) sum -= pa[i - 1];
                maxSum_ = max(maxSum_, sum);
            }
            maxSum[d] = maxSum_;
        }

        for (int k = 0; k <= N; ++k) {
            long long answer = 0;
            for (int d = 0; d <= N; ++d) {
                answer = max(answer, maxSum[d] + 1LL * min(d, k) * X);
            }
            cout << answer << ' ';
        }
        cout << '\n';
    }
}

 

D. Cross Coloring

더보기

풀이

  • 각 칸에 대해서 가장 마지막으로 행한 operation 의 번호를 매기고 모든 칸에서 distinct 한 operation 번호의 수를 p 라 하면 각 p 개의 그룹에 색의 개수 k 개씩 칠하는 경우가 있으므로 답은 $k^p$ 이다
  • operation 의 역순으로 생각해보자
    • 이전에 칠해진거 밑에 칠해진다
    • 다 채워진 뒤에는 더 이상 칸들의 마지막 operation 번호에 변화가 있을 수 없다.
  • $r[x], c[y]$ 각각 x행/y열 이 이미 다른 번호에의해 칠해졌는가 라고 정의하면
    • 지금 수행할 operation 의 x, y 에 대해
      • r[x] = true && c[y] = true 이면 변화가 없음
      • 하나라도 false 면 가능
      • 또한 전체행이나 전체열이 채워지면 더 해볼 필요가 없음

코드

  • 시간복잡도: $O(Q)$
  • 주의할점: testcase 에 대한 n, m 의 제한이 없어서 testcase 마다 vector r, c 를 잡으면 이거때문에 시간초과남
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int TC; cin >> TC;

    vector<int> r(2e5 + 1, -1), c(2e5 + 1, -1);
    while (TC--) {
        int N, M, K, Q;
        cin >> N >> M >> K >> Q;

        vector<pair<int, int> > operation(Q);
        for (int i = 0; i < Q; ++i) {
            cin >> operation[Q - 1 - i].first >> operation[Q - 1 - i].second;
        }

        int rr, cc;
        rr = cc = 0;
        long long answer = 1;
        long long MOD = 998244353;
        for (auto [x, y]: operation) {
            if (rr == N || cc == M) break;
            if (r[x] == TC && c[y] == TC) continue;
            answer = answer * K % MOD;
            if (r[x] != TC) r[x] = TC, rr++;
            if (c[y] != TC) c[y] = TC, cc++;
        }

        cout << answer << '\n';
    }
}

 

E. Expand the Path

더보기

풀이

  • 움직이는 칸의 범위는 처음 R, 처음 D 를 duplicate 한 칸들로 둘러 싸여있는 형태
    • 한 종류만 있는 경우에는 그냥 N
  • 칸 범위 구하기
    1. 초기 칸 수
    2. 아래로 한칸씩 내릴때(duplicate D) 추가되는 칸 수(정사영 내렸을때 칸 수) * (내릴 수 있는 칸 수)
      • 추가되는 칸 수 계산할 때 처음 R, D 에 유의
    3. 아래로 다 내린상태에서 옆으로 옮길때 추가되는 칸 수 * 옆으로 옮길수 있는 칸 수

코드

  • 시간복잡도: $O(s.size())$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        long long N;
        cin >> N;
        string s;
        cin >> s;

        long long r, d, fr, fd;
        r = d = fr = fd = 0;

        for (int i = 0; i < s.size();) {
            if (s[i] == 'R') {
                int tmp = 0;
                while (i < s.size() && s[i] == 'R') ++tmp, ++i;
                r += tmp;
                if (fr == 0) fr += tmp;
            } else {
                int tmp = 0;
                while (i < s.size() && s[i] == 'D') ++tmp, ++i;
                d += tmp;
                if (fd == 0) fd += tmp;
            }
        }

        if (r == 0 || d == 0) {
            cout << N << '\n';
            continue;
        }
        
        long long answer = 0;
        if (s[0] == 'D') {
            answer = (r + d + 1);
            answer += (r + 1) * (N - d - 1);
            answer += (d - fd + 1 + N - d - 1) * (N - r - 1);
        } else {
            answer = (r + d + 1);
            answer += (r - fr + 1) * (N - d - 1);
            answer += (d + 1 + N - d - 1) * (N - r - 1);
        }
        cout << answer << '\n';
    }
}

 

728x90

문제링크

https://www.acmicpc.net/problem/10266

 

10266번: 시계 사진들

상근이는 보통의 시계와는 다른 독특한 시계 사진 두장이 있습니다. 시계는 n개의 동일한 길이와 목적을 가진 시계 바늘들을 가지고 있습니다. 애석하게도 시계의 숫자들은 희미해져 각 시계 바

www.acmicpc.net

풀이

  • 시계 시간이 같다는건 각 시계의 바늘들의 위치관계가 같다는 것
  • $v[x]:$ x 각도에 있는지 없는지로 정의
  • v1 이랑 v2 를 shift 한거랑 같을 때가 있는지 찾기
    • 는 v1 두개 붙인 배열에서 v2 찾기랑 같음

코드

  • 시간복잡도: 
    • $v[x]: $ x 각도 바늘 유무로 하면 $O(360,000 * 3 + 2N)$
    • $v[i]: $ 각도 오름차순 기준으로 i + 1 번째 바늘과 i 번째 바늘의 차이로 정의하면 $O(2N + NlogN + N + 2N)$
    • 문제조건에선 첫번째로 하는게 살짝 빠름
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin >> N;
    
    vector<int> a(360000), b(360000);
    for (int i = 0; i < N; ++i) {
        int num;
        cin >> num;
        a[num] = 1;
    }
    for (int i = 0; i < N; ++i) {
        int num;
        cin >> num;
        b[num] = 1;
    }

    auto buildPi = [&](vector<int>& p) {
        int pLen = p.size();
        vector<int> pi(pLen, 0);
        for (int j = 0, i = 1; i < pLen; ++i) {
            while (j > 0 && p[i] != p[j]) j = pi[j - 1];
            if (p[i] == p[j]) ++j, pi[i] = j;
        }
        return pi;
    };
    auto kmp = [&](vector<int>& s, vector<int>& p) {
        // find p in s
        vector<int> answer;
        int sLen = s.size(), pLen = p.size();
        auto pi = buildPi(p);
        for (int j = 0, i = 0; i < 2 * sLen; ++i) {
            while (j > 0 && s[i % sLen] != p[j]) j = pi[j - 1];
            if (j == pLen - 1) answer.push_back(i % sLen - pLen + 2), j = pi[j];
            else ++j;
        }
        return answer;
    };

    auto answer = kmp(a, b);
    if (answer.size()) {
        cout << "possible\n";
    } else {
        cout << "impossible\n";
    }
}
728x90

'PS > BOJ' 카테고리의 다른 글

백준 10254번: 고속도로  (0) 2022.02.26
백준 13547번: 수열과 쿼리 5  (0) 2022.02.25
백준 11408번: 열혈강호 5  (0) 2022.02.22
백준 9248번: Suffix Array  (0) 2022.02.21
백준 3033번: 가장 긴 문자열  (0) 2022.02.20

문제링크

https://www.acmicpc.net/problem/11408

 

11408번: 열혈강호 5

강호네 회사에는 직원이 N명이 있고, 해야 할 일이 M개가 있다. 직원은 1번부터 N번까지 번호가 매겨져 있고, 일은 1번부터 M번까지 번호가 매겨져 있다. 각 직원은 한 개의 일만 할 수 있고, 각각

www.acmicpc.net

풀이

  • 최대 유량문제에서 증가경로를 찾을 때 cost 에 대해 최단 경로로 찾음
  • 역방향 간선의 cost 가 음수이므로 음수간선에 대해서도 동작해야함
    • SPFA

코드

  • 시간복잡도: $O(VEf)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

struct Edge {
    int to, c, f, cost;
    Edge* reverse;

    Edge() {
        to = c = f = cost = 0;
        reverse = nullptr;
    }
    Edge(int to_, int c_, int cost_) {
        to = to_;
        c = c_;
        cost = cost_;
        f = 0;

        reverse = nullptr;
    }

    int reisidual() {
        return c - f;
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, M;
    cin >> N >> M;

    vector<int> A(N + 1, 0), B(M + 1, 0);
    vector<vector<Edge*> > adj(N + M + 2);
    int S = 0;
    int T = adj.size() - 1;
    auto makeEdge = [&](int u, int v, int c, int cost) {
        auto e1 = new Edge(v, c, cost);
        auto e2 = new Edge(u, 0, -cost);
        e1 -> reverse = e2;
        e2 -> reverse = e1;
        adj[u].push_back(e1);
        adj[v].push_back(e2);
    };
    
    for (int i = 1; i <= N; ++i) {
        makeEdge(S, i, 1, 0);
        int cnt = 0;
        cin >> cnt;
        while (cnt--) {
            int v, cost;
            cin >> v >> cost;
            makeEdge(i, N + v, 1, cost);
        }
    }
    for (int i = 1; i <= M; ++i) {
        makeEdge(N + i, T, 1, 0);
    }
    
    int maxFlow = 0;
    int minCost2 = 0;
    while (true) {
        vector<Edge*> prev(N + M + 2, nullptr);
        queue<int> q;
        vector<int> inQ(N + M + 2, false);
        vector<int> minCost(N + M + 2, 1e9);
        q.push(S);
        minCost[S] = 0;
        inQ[S] = true;
        while (q.size()) {
            int cur = q.front();
            q.pop();
            inQ[cur] = false;
            for (auto e: adj[cur]) {
                if (e -> reisidual() > 0 && minCost[e -> to] > minCost[cur] + e -> cost) {
                    minCost[e -> to] = minCost[cur] + e -> cost;
                    prev[e -> to] = e;
                    if (!inQ[e -> to]) {
                        q.push(e -> to);
                        inQ[e -> to] = true;
                    }
                }
            }
        }

        if (!prev[T]) break;
        
        int flow = 1e9;
        for (auto cur = T; cur != S; cur = prev[cur] -> reverse -> to) {
            flow = min(flow, prev[cur] -> reisidual());
        }

        for (auto cur = T; cur != S; cur = prev[cur] -> reverse -> to) {
            prev[cur] -> f += flow;
            prev[cur] -> reverse -> f -= flow;
        }
        maxFlow += flow;
        minCost2 += minCost[T];
    }

    cout << maxFlow << '\n';
    cout << minCost2 << '\n';
}
728x90

'PS > BOJ' 카테고리의 다른 글

백준 13547번: 수열과 쿼리 5  (0) 2022.02.25
백준 10266번: 시계 사진들  (0) 2022.02.23
백준 9248번: Suffix Array  (0) 2022.02.21
백준 3033번: 가장 긴 문자열  (0) 2022.02.20
백준 2316번: 도시 왕복하기 2  (0) 2022.02.20

https://codeforces.com/contest/1635

 

Dashboard - Codeforces Round #772 (Div. 2) - Codeforces

 

codeforces.com

 

A. Min Or Sum

더보기

풀이

  • 배열 원소들의 or 값은 일정
  • 0 아닌 두 수를 0 과 $a_i$ $|$ $a_j$ 로 바꾸자

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;
        vector<long long> a(N);
        long long answer = 0;
        for (auto& e: a) {
            cin >> e;
            answer |= e;
        }
        cout << answer << '\n';
    }
}

 

B. Avoid Local Maximums

더보기

풀이

  • 극대점 다음 원소를 그 원소에 인접한 두 원소들 중 최대로 바꾸면 극대점을 최대 2개까지 없앨 수 있다

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;
        vector<int> a(N);
        for (auto& e: a) {
            cin >> e;
        }

        int answer = 0;
        for (int i = 1; i < N - 1; ++i) {
            if (a[i] > a[i - 1] && a[i] > a[i + 1]) {
                if (i == N - 2) a[i + 1] = a[i], ++answer;
                else a[i + 1] = max(a[i], a[i + 2]), ++answer;
            }
        }

        cout << answer << '\n';
        for (auto& e: a) {
            cout << e << ' ';
        }
        cout << '\n';
    }
}

 

C. Differential Sorting

더보기

풀이

  • $x$ 최대가 $N - 2$ 이므로 $a_{N - 1} <= a_{N}$ 이어야 함 아니면 불가능
  • 뒤의 두개가 정렬된 상태일 때
    • $a_N >= 0$ 이면 $a_{N - 1} - a_{N} <= a_{N - 1} <= a_{N}$ 성립
      • 나머지를 다 $a_{N - 1} - a_{N}$ 으로 바꿈
    • $a_N < 0$ 이면 모두 정렬된 상태가 아니면 불가능함
      • operation 후 정렬된 상태라면 모두 음수인데 음수에 대해서 정렬된 상태로 만들 수 없음

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;
        vector<long long> a(N);
        for (auto& e: a) {
            cin >> e;
        }

        bool no = false;

        vector<tuple<int, int, int> > answer;
        if (a[N - 2] > a[N - 1]) {
            no = true;
        } else if (a[N - 1] >= 0) {
            for (int i = 0; i + 2 < N; ++i) {
                answer.push_back({i + 1, N - 1, N});
            }
        } else {
            if (!is_sorted(all(a))) {
                no = true;
            }
        }

        if (no) {
            cout << "-1\n";
        } else {
            cout << answer.size() << '\n';
            for (auto [x, y, z]: answer) {
                cout << x << ' ' << y << ' ' << z << '\n';
            }
        }
    }
}

 

D. Infinite Set

더보기

풀이

  • 서로 다른 수로부터 생성되는 수들은 모두 distinct 함
  • $f(x) = k, 2^k <= x <= 2^{k + 1}$ 라고할 때
    • $f(2x + 1) = k + 1$ 이고 $f(4x) = k + 2$ 이다.
  • 최초 배열에서 제일 근본적인 수만 남기고 그 원소들에 대해서 dp (최소 스패닝)

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, P;
    cin >> N >> P;

    set<int> s;
    for (int i = 0; i < N; ++i) {
        int num;
        cin >> num;
        s.insert(num);
    }

    vector<int> v;
    for (auto it = s.rbegin(); it != s.rend(); ++it) {
        bool del = false;
        auto tmp = *it;
        while (tmp > 0) {
            if (tmp != *it && s.count(tmp)) {
                del = true;
                break;
            }
            if (tmp & 1) {
                tmp >>= 1;
            } else if (tmp % 4 == 0) {
                tmp >>= 2;
            } else {
                break;
            }
        }
        if (!del) {
            v.push_back(*it);
        }
    }
    reverse(all(v));
    vector<long long> dp(P, 0);
    for (long long tmp = 1, i = 0; i < P && tmp <= v.back(); ++i, tmp <<= 1LL) {
        dp[i] += lower_bound(all(v), tmp * 2) - lower_bound(all(v), tmp);
    }

    long long MOD = 1e9 + 7;
    long long answer = 0;
    for (int i = 0; i < P; ++i) {
        if (i + 1 < P) dp[i + 1] += dp[i], dp[i + 1] %= MOD;
        if (i + 2 < P) dp[i + 2] += dp[i], dp[i + 2] %= MOD;
        answer += dp[i];
        answer %= MOD;
    }

    cout << answer << '\n';
}

 

728x90

https://atcoder.jp/contests/abc240/tasks

 

Tasks - AtCoder Beginner Contest 240

AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.

atcoder.jp

 

A - Edge Checker

더보기

풀이

  • 작은 수 + 1 == 큰 수 임을 체크한다.
  • 작은 수 1 , 큰 수 10 케이스에 주의

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int a, b;
    cin >> a >> b;

    if (a > b) swap(a, b);

    if (a == 1 && b == 10) {
        cout << "Yes\n";
    } else if (a + 1 == b) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
}

 

B - Count Distinct Integers

더보기

풀이

  • distinct 원소 개수 구하기
  • set 에 insert

코드

  • 시간복잡도: $O(NlogN)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin >> N;
    set<int> s;
    for (int i = 0; i < N; ++i) {
        int num;
        cin >> num;
        s.insert(num);
    }
    cout << s.size() << '\n';
}

 

 

C - Jumping Takahashi

더보기

풀이

  • $dp[i][j]: i$번 점프후 $j$위치에 있는 것이 가능하면 $1$ 아니면 $0$
  • $dp[i][j] = 1$ 이면 $dp[i + 1][j + a[i + 1]] = dp[i + 1][j + b[i + 1]] = 1$

코드

  • 시간복잡도: $O(NX)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, X;
    cin >> N >> X;

    vector<vector<long long> > dp(N, vector<long long> (100 * 100 + 1, - 1));
    for (int i = 0; i < N; ++i) {
        int a, b;
        cin >> a >> b;
        if (i == 0) {
            dp[i][a] = 1;
            dp[i][b] = 1;
            continue;
        }
        for (int j = 0; j <= 100 * 100; ++j) {
            if (dp[i - 1][j] == -1) continue;
            dp[i][j + a] = dp[i][j + b] = 1; 
        }
    }

    if (dp[N - 1][X] == 1) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
}

 

D - Strange Balls

더보기

풀이

  • 스택으로 {번호, 갯수} 를 관리
  • top 과 같은 번호 들어오면 지울지 말지 결정
  • 다른 번호들어오면 push

코드

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin >> N;

    stack<pair<int, int> > st;
    int answer = 0;
    for (int i = 0; i < N; ++i) {
        int num;
        cin >> num;

        if (st.size()) {
            if (st.top().first == num) {
                if (st.top().second == num - 1) {
                    st.pop();
                    answer -= num - 1;
                } else {
                    int tmp = st.top().second;
                    st.pop();
                    st.push({num, tmp + 1});
                    ++answer;
                }
            } else {
                st.push({num, 1});
                ++answer;
            }
        } else {
            st.push({num, 1});
            ++answer;
        }
        cout << answer << '\n';
    }
}

 

E - Ranges on Tree

더보기

풀이

  • dfs 하면서 리프 노드(간선이 부모랑밖에 없는 노드) 의 L = R = seq++
  • 리프 노드가 아니면 자식노드들의 구간 모두 포함하게 구성

코드

  • 시간복잡도: $O(N)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin >> N;
    vector<vector<int> > adj(N + 1);
    for (int i = 0; i < N - 1; ++i) {
        int u, v;
        cin >> u >> v;

        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    
    vector<int> L(N + 1, 0), R(N + 1, 0);
    int seq = 1;
    function<void(int, int)> f = [&](int cur, int p) {
        if (adj[cur].size() == 1 && adj[cur][0] == p) {
            L[cur] = R[cur] = seq++;
            return;
        }
        int l = 1e9;
        int r = -1e9;
        for (auto e: adj[cur]) {
            if (e == p) continue;
            if (!L[e] && !R[e]) {
                f(e, cur);
            }
            l = min(l, L[e]);
            r = max(r, R[e]);
        }
        L[cur] = l;
        R[cur] = r;
    };

    f(1, 0);
    for (int i = 1; i <= N; ++i) {
        cout << L[i] << ' ' << R[i] << '\n';
    }
}

 

F - Sum Sum Max

더보기

풀이

  • 각 원소 반복의 끝, 또는 그 부분의 B 값이 양수이면 B 값이 음수 직전까지 의 A 값이 최대이다

코드

  • 시간복잡도: $O(N)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        long long N, M;
        cin >> N >> M;
        vector<pair<long long, long long> > C(N);
        vector<long long> A(N, -1e9), B(N, -1e9);
        for (int i = 0; i < N; ++i) {
            long long x, y;
            cin >> x >> y;
            C[i] = {x, y};
            B[i] = x * y;
            if (i) B[i] += B[i - 1];
            A[i] = x * y * (y + 1) / 2;
            if (i) A[i] += A[i - 1] + B[i - 1] * y;
        }

        long long answer = C[0].first;
        for (int i = 0; i < N; ++i) {
            if (i < N - 1 && C[i + 1].first < 0 && B[i] > 0) {
                long long k = B[i] / abs(C[i + 1].first);
                k = min(k, C[i + 1].second);
                answer = max(answer, A[i] + k * B[i] + k * (k + 1) * C[i + 1].first / 2);
            } else {
                answer = max(answer, A[i]);
            }
        }
        cout << answer << '\n';
    }
}
728x90

'PS > Atcoder' 카테고리의 다른 글

[앳코더] AtCoder Beginner Contest 241(비기너 241) 풀이  (0) 2022.03.03

https://codeforces.com/contest/1638

 

Dashboard - Codeforces Round #771 (Div. 2) - Codeforces

 

codeforces.com

 

A. Reverse

더보기

풀이

  • 오름차순으로 되어있을 때 사전 순으로 맨 앞이다.
  • 오름차순 정렬된 상태와 처음으로 같지 않을 때 그 위치에 있어야 할 수가 그 위치에 오도록 reverse 하지 않으면 그렇게 한 것보다 사전 순으로 뒤에 있다.
  • 따라서 그 위치에 오도록 reverse 하는게 사전 순으로 맨 앞이다.

코드

  • 시간복잡도: $O(N)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;

        vector<int> a(N);
        for (auto& e: a) {
            cin >> e;
        }
        vector<int> tmp(a);
        sort(all(tmp));

        for (int i = 0; i < N; ++i) {
            if (a[i] != i + 1) {
                auto it = find(a.begin() + i + 1, a.end(), i + 1);
                reverse(a.begin() + i, it + 1);
                break;
            }
        }
        for (auto& e: a) {
            cout << e << ' ';
        }
        cout << '\n';
    }
}

 

 

B. Odd Swap Sort

더보기

풀이

  • 홀수, 짝수 사이는 자유롭게 스왑가능하다.
  • 홀수, 홀수 이거나 짝수, 짝수 이면 스왑불가능하다.
  • 따라서 operation 후 홀수들간의 위치관계 와 짝수들간의 위치관계는 각각 유지된다.
  • 짝수, 홀수 들이 각각 정렬된 상태이면 전체를 정렬할 수 있다.

코드

  • 시간복잡도: $O(N)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;
        vector<long long> odd, even;

        for (int i = 0; i < N; ++i) {
            int num;
            cin >> num;
            if (num & 1) {
                odd.push_back(num);
            } else {
                even.push_back(num);
            }
        }

        if (is_sorted(all(odd)) && is_sorted(all(even))) {
            cout << "Yes\n";
        } else{
            cout << "No\n";
        }
    }
}

 

 

C. Inversion Graph

더보기

풀이

  • Disjoint set 자료구조에서 각 셋의 최댓값을 루트로 한다.
  • 배열 원소 차례로 순회하면서 이전 set 들의 최댓값들 중 지금 원소보다 큰 값이 있으면 union 한다.

코드

  • 시간복잡도: 아마 $O(NlogN)$

※ 스택으로 연결된 그룹의 min, max 관리하면 $O(N)$ 으로 처리 가능

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()



int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int TC; cin >> TC;
    while (TC--) {
        int N;
        cin >> N;
        vector<int> a(N);
        for (auto& e: a) {
            cin >> e;
        }

        vector<int> p(N + 1, -1);
        function<int(int)> myFind = [&](int n) {
            if (p[n] == -1) return n;
            return p[n] = myFind(p[n]);
        };
        set<int> s;
        auto myUnion = [&](int n1, int n2) {
            int p1 = myFind(n1);
            int p2 = myFind(n2);

            if (p1 == p2) return;
            if (p1 > p2) swap(p1, p2);
            p[p1] = p2;
            s.erase(p1);
        };

        int answer = N;
        s.insert(a[0]);
        for (int i = 1; i < N; ++i) {
            auto it = s.upper_bound(a[i]);
            if (it != s.end()) {
                while (it != s.end()) {
                    myUnion(a[i], *it);
                    --answer;
                    it = s.upper_bound(myFind(a[i]));
                }
            } else {
                s.insert(a[i]);
            }
        }
        cout << answer << '\n';
    }
}

 

D. Big Brush

더보기

풀이

  • 색칠순서를 역순으로 구성한다.
  • 처음에는 4칸이 모두 같은색인 곳을 칠한다.
  • 이전에 칠한곳은 지금칠할 곳을 덮어쓰므로 어떤색이어도 상관없다.
  • 이전에 칠한곳 근처에서 4칸 중 이전에 칠한곳을 제외한 칸들이 모두 같은색이면 칠한다.

코드

  • BFS
  • 시간복잡도: $O(NM)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, M;
    cin >> N >> M;

    vector<vector<int> > a(N, vector<int> (M));
    vector<vector<int> > b(N, vector<int> (M, 0));

    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < M; ++j) {
            cin >> a[i][j];
        }
    }

    queue<tuple<int, int, int> > q;
    auto check = [&](int i, int j) {
        if (i < 0 || i >= N - 1 || j < 0 || j >= M - 1) return 0;
        set<int> color;
        for (int ii = 0; ii < 2; ++ii) {
            for (int jj = 0; jj < 2; ++jj) {
                if (!b[i + ii][j + jj]) {
                    color.insert(a[i + ii][j + jj]);
                }
            }
        }

        if (color.size() == 1) {
            return *color.begin();
        } else {
            return 0;
        }
    };

    vector<vector<int> > visited(N - 1, vector<int> (M - 1, false));
    for (int i = 0; i < N - 1; ++i) {
        for (int j = 0; j < M - 1; ++j) {
            auto c = check(i, j);
            if (c) {
                q.push({i, j, c});
                visited[i][j] = true;
            }
        }
    }

    vector<tuple<int, int, int> > answer;
    int dr[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
    int dc[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
    while (q.size()) {
        int cnt = q.size();
            auto [i, j, c] = q.front();
            q.pop();
            answer.push_back({i, j, c});
            b[i][j] = b[i + 1][j] = b[i][j + 1] = b[i + 1][j + 1] = true;
            for (int dir = 0; dir < 8; ++dir) {
                int ni = i + dr[dir];
                int nj = j + dc[dir];
                if (ni < 0 || ni >= N - 1 || nj < 0 || nj >= M - 1 || visited[ni][nj]) continue;
                auto nc = check(ni, nj);
                if (nc) {
                    q.push({ni, nj, nc});
                    visited[ni][nj] = true;
                }
            }
    }

    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < M; ++j) {
            if (!b[i][j]) {
                cout << "-1\n";
                return 0;
            }
        }
    }

    reverse(all(answer));
    cout << answer.size() << '\n';
    for (auto [i, j, c]: answer) {
        cout << i + 1 << ' ' << j + 1 << ' ' << c << '\n';
    }
}

 

728x90

문제링크

https://www.acmicpc.net/problem/9248

 

9248번: Suffix Array

Suffix Array란, 문자열 S가 있을 때 그 접미사들을 정렬해 놓은 배열이다. 예를 들어, 문자열 S=banana의 접미사는 아래와 같이 총 6개가 있다. Suffix i banana 1 anana 2 nana 3 ana 4 na 5 a 6 이를 Suffix 순으로 정

www.acmicpc.net

풀이

  • SA 구하기
    • suffix 들을 앞부분 길이 1, 2, 4, 8 ... 에 대해서 정렬
    • 앞부분 길이 d 에서 정렬한 정보를 2 * d 를 정렬할 때 이용할 수 있음
  • LCP 구하기 (Kasai's algorithm)
    • 인덱스 0 시작, 1시작, ... 접미사들에 대해서 차례로 lcp 를 구한다
    • $lcp[isa[i]] = k$ 이면 $lcp[isa[i + 1]] \geq k - 1$ 임을 이용한다

코드

  • 시간복잡도
    • 문자열길이: $N$ 
    • SA: $O(Nlog^2N)$
      • counting sort 로 정렬시 $O(NlogN)$
    • LCP: $O(O(SA) + N)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    string s;
    cin >> s;

    auto buildSA = [&](string& s) {
        int N = s.size();
        vector<int> sa(N), r(2 * N, 0), nr(2 * N);
        for (int i = 0; i < N; ++i) {
            sa[i] = i;
            r[i] = s[i];
        }

        for (int d = 1; d < N; d <<= 1) {
            auto cmp = [&](int i, int j) {
                return r[i] < r[j] || (r[i] == r[j] && r[i + d] < r[j + d]);
            };
            sort(all(sa), cmp);
            nr[sa[0]] = 1;
            for (int i = 1; i < N; ++i) {
                nr[sa[i]] = nr[sa[i - 1]] + cmp(sa[i - 1], sa[i]);
            }
            r = nr;
        }
        for (auto& e: sa) {
            cout << e + 1 << ' ';
        }
        cout << '\n';
        return sa;
    };

    auto buildLCP = [&](string& s) {
        int N = s.size();
        auto sa = buildSA(s);
        vector<int> isa(N), lcp(N);
        for (int i = 0; i < N; ++i) {
            isa[sa[i]] = i;
        }

        for (int k = 0, i = 0; i < N; ++i) {
            if (isa[i] == 0) continue;
            for (int j = sa[isa[i] - 1]; max(i, j) + k < N && s[i + k] == s[j + k]; ++k);
            lcp[isa[i]] = k ? k-- : 0;

        }
        return lcp;
    };

    auto lcp = buildLCP(s);

    for (int i = 0; i < lcp.size(); ++i) {
        if (i) {
            cout << lcp[i] << ' ';
        } else {
            cout << "x ";
        }
    }
}
728x90

'PS > BOJ' 카테고리의 다른 글

백준 10266번: 시계 사진들  (0) 2022.02.23
백준 11408번: 열혈강호 5  (0) 2022.02.22
백준 3033번: 가장 긴 문자열  (0) 2022.02.20
백준 2316번: 도시 왕복하기 2  (0) 2022.02.20
백준 13537번: 수열과 쿼리 1  (0) 2022.02.19

문제 링크

https://www.acmicpc.net/problem/3033

 

3033번: 가장 긴 문자열

첫째 줄에 L이 주어진다. (1 ≤ L ≤ 200,000) 다음 줄에는 길이가 L이면서 알파벳 소문자로 이루어진 문자열이 주어진다.

www.acmicpc.net

풀이 1. Hashing, Rabin-Karp algorithm, Parametric search

  • 길이 $l$ 인 부분 문자열이 전체 문자열내에서 두번 이상 등장한다고 하자
  • 그 부분 문자열의 부분 문자열들(길이 $1, 2, ..., l$) 또한 전체 문자열내에서 두번 이상 등장한다
  • 따라서 결정문제: 길이 $l$ 부분문자열이 2번이상 등장하는가? 에 대한 답의 분포는 이분적이다($T, ..., T, F, F, ..., F$)
  • 각 결정문제를 해결할 때에는 라빈카프 알고리즘의 롤링해쉬함수를 이용한다

롤링해쉬함수
롤링해쉬함수

코드 1.

  • 시간복잡도: $O(LlogL)$
    • Parametric search: $O(logL)$
    • 결정문제: $O(L)$
#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int L;
    cin >> L;
    string s;
    cin >> s;

    long long MOD = 1e5 + 3;
    long long MUL = 2;

    auto check = [&](int l) {
        vector<vector<int> > hashTable(MOD);
        long long h = 0;
        long long MUL_POW_L = 1;
        for (int i = 0; i < l; ++i) {
            h *= MUL;
            h += s[i] - 'a' + 1;
            h %= MOD;
            MUL_POW_L *= MUL;
            MUL_POW_L %= MOD;
        }
        hashTable[h].push_back(0);

        for (int i = 1; i + l - 1 < L; ++i) {
            h *= MUL;
            h %= MOD;
            h -= (1LL * (s[i - 1] - 'a' + 1) * MUL_POW_L) % MOD;
            h += s[i + l - 1] - 'a' + 1; 
            h %= MOD;
            if (h < 0) h += MOD;

            for (auto idx: hashTable[h]) {
                if (s.compare(i, l, s, idx, l) == 0) return true;
            }
            hashTable[h].push_back(i);
        }
        return false;
    };

    int lo = 0;
    int hi = L;

    while (lo + 1 < hi) {
        int mid = lo + hi >> 1;
        if (check(mid)) lo = mid;
        else hi = mid;
    }

    cout << lo << '\n';
}

 

풀이 2. Suffix array and LCP array

  • $*max\_element(all(lcp)) = lcp[i]$ 일 때
    • 반복 문자열 최대길이 $= lcp[i]$
    • 반복 문자열 $= s[sa[i]:sa[i] + lcp[i] - 1]$

코드 2.

#include <bits/stdc++.h>

using namespace std;

#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int L;
    cin >> L;
    string s;
    cin >> s;

    auto buildSA = [&](string& s) {
        int N = s.size();
        vector<int> sa(N), r(2 * N), nr(2 * N);
        for (int i = 0; i < N; ++i) {
            sa[i] = i;
            r[i] = s[i];
        }
        for (int d = 1; d < N; d <<= 1) {
            auto cmp = [&](int i, int j) {
                return r[i] < r[j] || (r[i] == r[j] && r[i + d] < r[j + d]);
            };
            sort(all(sa), cmp);
            for (int i = 1; i < N; ++i) {
                nr[sa[i]] = nr[sa[i - 1]] + cmp(sa[i - 1], sa[i]);
            }
            r = nr;
        }
        return sa;
    };

    auto buildLCP = [&](string& s) {
        int N = s.size();
        auto sa = buildSA(s);
        vector<int> lcp(N), isa(N);
        for (int i = 0; i < N; ++i) {
            isa[sa[i]] = i;
        }
        for(int k = 0, i = 0;i < N;++i) {
            if(isa[i]){
                for(int j = sa[isa[i]-1]; max(i, j) + k < N && s[i+k] == s[j+k]; ++k);
                lcp[isa[i]] = (k ? k-- : 0);
            }
        }
        return lcp;
    };

    auto lcp = buildLCP(s);
    cout << *max_element(all(lcp)) << '\n';
}
728x90

'PS > BOJ' 카테고리의 다른 글

백준 11408번: 열혈강호 5  (0) 2022.02.22
백준 9248번: Suffix Array  (0) 2022.02.21
백준 2316번: 도시 왕복하기 2  (0) 2022.02.20
백준 13537번: 수열과 쿼리 1  (0) 2022.02.19
백준 1126번: 같은 탑  (0) 2022.02.18

+ Recent posts