Processing math: 100%

문제링크

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

 

10266번: 시계 사진들

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

www.acmicpc.net

풀이

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

코드

  • 시간복잡도: 
    • v[x]: x 각도 바늘 유무로 하면 O(360,0003+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 과 ai | aj 로 바꾸자

코드

#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 최대가 N2 이므로 aN1<=aN 이어야 함 아니면 불가능
  • 뒤의 두개가 정렬된 상태일 때
    • aN>=0 이면 aN1aN<=aN1<=aN 성립
      • 나머지를 다 aN1aN 으로 바꿈
    • aN<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,2k<=x<=2k+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]]k1 임을 이용한다

코드

  • 시간복잡도
    • 문자열길이: N 
    • SA: O(Nlog2N)
      • 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

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

 

2316번: 도시 왕복하기 2

N개의 도시가 P개의 양방향 길로 연결되어 있다. 이석원은 1번 도시와 2번 도시 사이를 오가며 워해머를 한다. 성실한 이석원은 두 도시 사이를 최대한 많이 왔다 갔다 하려 하는데, 이때 한 번 방

www.acmicpc.net

풀이

  • 1, 2번 제외한 정점들은 한번씩만 지날 수 있다 -> 용량 1 인 간선으로 각 정점분할
    • u 의 in 정점 : 2u - 1, out 정점: 2u
    • in -> out 단방향 간선 용량 1
    • 1, 2번 정점은 여러번 지날 수 있어야 하므로 (in -> out) 간선 용량은 400(최대 경로 수) 로 지정
  • u - v : 양방향 간선을 두개의 단방향 간선으로 (u out -> v in, v out -> u in)

정점 모델링

코드

#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;
Edge* reverse;
Edge() {
to = c = f = 0;
reverse = nullptr;
}
Edge(int to_, int c_) {
f = 0;
c = c_;
to = to_;
reverse = nullptr;
}
int residual() {
return c - f;
}
};
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, P;
cin >> N >> P;
vector<vector<Edge*> > adj(2 * N + 1);
auto makeEdge = [&](int u, int v, int c) {
auto e1 = new Edge(v, c);
auto e2 = new Edge(u, 0);
e1 -> reverse = e2;
e2 -> reverse = e1;
adj[u].push_back(e1);
adj[v].push_back(e2);
};
for (int i = 0; i < P; ++i) {
int u, v;
cin >> u >> v;
makeEdge(2 * u, 2 * v - 1, 1);
makeEdge(2 * v, 2 * u - 1, 1);
}
for (int i = 1; i <= N; ++i) {
int c = 1;
if (i == 1 || i == 2) c = 400;
makeEdge(2 * i - 1, 2 * i, c);
}
int S = 1 * 2 - 1;
int T = 2 * 2;
int answer = 0;
while (true) {
vector<Edge*> prev(2 * N + 1, nullptr);
queue<int> q;
q.push(S);
while (q.size()) {
auto f = q.front();
q.pop();
for (auto e: adj[f]) {
if (prev[e -> to]) continue;
if (e -> residual() <= 0) continue;
q.push(e -> to);
prev[e -> to] = e;
if (e -> to == T) break;
}
if (prev[T]) break;
}
if (!prev[T]) break;
int flow = 1e9;
for (int cur = T; cur != S; cur = prev[cur] -> reverse -> to) {
flow = min(flow, prev[cur] -> residual());
}
for (int cur = T; cur != S; cur = prev[cur] -> reverse -> to) {
prev[cur] -> f += flow;
prev[cur] -> reverse -> f -= flow;
}
answer += flow;
}
cout << answer << '\n';
}
728x90

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

백준 9248번: Suffix Array  (0) 2022.02.21
백준 3033번: 가장 긴 문자열  (0) 2022.02.20
백준 13537번: 수열과 쿼리 1  (0) 2022.02.19
백준 1126번: 같은 탑  (0) 2022.02.18
백준 11377번: 열혈강호 3  (0) 2022.02.18

+ Recent posts