Notice
Recent Posts
Recent Comments
Link
«   2025/09   »
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
Archives
Today
Total
관리 메뉴

승형님의 블로그

Atcoder Beginner Contest 280 본문

Problem Solving/Atcoder

Atcoder Beginner Contest 280

승형 2022. 12. 8. 10:47

 

https://atcoder.jp/contests/abc280

 

 

Denso Create Programming Contest 2022 Winter(AtCoder Beginner Contest 280) - AtCoder

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

atcoder.jp

 

 

A. Pawn on a Grid

 

 

요약

 

 

주어진 격자에서 '#'의 개수를 세어 반환한다.


#include <bits/stdc++.h>
using namespace std;
 
int H, W, cnt;
 
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> H >> W;
	for (int i = 0; i < H; i++) {
		string s;
		cin >> s;
		for (int j = 0; j < W; j++) if (s[j] == '#') cnt++;
	}
	cout << cnt;
	return 0;
}

 

 

 

B. Inverse Prefix Sum

 

 

요약

 

 

N과 수열 S가 주어질 때, 다음을 만족하는 수열 A를 구한다.
$A_{1} + A_{2} + ... + A_{k}  = S_{k}$


1. A[1] = S[1]로 고정된다.

 

2. A[k]는 필연적으로 S[k] - S[k-1]으로 고정됨을 알 수 있다.

#include <bits/stdc++.h>
using namespace std;
 
int N;
long long S[10];
 
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> N;
	for (int i = 0; i < N; i++) cin >> S[i];
	cout << S[0] << " ";
	for (int i = 1; i < N; i++) cout << S[i] - S[i-1] << " ";	
	return 0;
}

 

 

 

C. Extra Character

 

 

요약

 

 

문자열 S와 T가 주어진다. T는 S에 하나의 알파벳 소문자를 추가하여 만든 문자열이다. 문자가 추가된 위치를 출력한다.


S와 T가 처음 달라지는 위치를 출력한다.

#include <bits/stdc++.h>
using namespace std;

string S, T;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> S >> T;
	for (int i = 0; i < S.length(); i++) {
		if (S[i] != T[i]) {
			cout << i + 1;
			return 0;
		}
	}
	cout << T.length();
	return 0;
}

 

 

 

D. Factorial and Multiple

 

 

요약

 

 

정수 K가 주어졌을 때, $N!$이 K의 배수가 되도록 하는 가장 작은 N을 구한다.


1. $N!$이 K의 배수가 되기 위해서는 $N!$이 K의 모든 소인수를 포함하고 있어야 한다.

 

2. $\sqrt{K}$ 까지 반복문을 통해 K를 소인수분해하고, 동시에 K의 소인수의 개수를 포함하기 위한 N의 최대값을 구한다.
https://int-p.tistory.com/21

 

정수의 소인수분해 알고리즘

정수 K를 소인수분해하여 다음과 같이 나타낼 수 있다. $K = p_{1}^{q_{1}}\times p_{2}^{q_{2}}...\times p_{i}^{q_{i}}\times M$ 이때, $p_{1}, p_{2} ... p_{i} \leq \sqrt{K}$ 이고, M은 소수이다. int K; vector primeFact; int main(void

int-p.tistory.com

#include <bits/stdc++.h>
using namespace std;

long long K, ans, cnt, x, temp;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> K;
	for (long long i = 2; i * i <= K; i++) {
		if (K % i == 0) {
			cnt = 0;
			while (1) {
				if (K % i != 0) break;
				K /= i;
				cnt++;
			}
			x = 0;
			while (cnt > 0) {
				x += i;
				temp = x;
				while (temp) {
					if (temp % i != 0) break;
					temp /= i;
					cnt--;
				}
			}
			ans = max(ans, x);
		}
	}
	ans = max(ans, K);
	cout << ans;
	return 0;
}

 

 

 

E. Critical Hit

 

 

요약

 

 

체력이 N인 몬스터가 있다. P%의 확률로 2의 타격을 줄 수 있고, (100-P)%의 확률로 1의 타격을 줄 수 있을 때, 몬스터의 체력을 0 이하로 깎기 위한 타격 횟수의 기댓값을 구한다.


1. DP의 관점으로 접근한다.

 

2. 체력을 i 깎는 경우는 체력을 i - 1 깎은 상태에서 $1 - P/100$의 확률로 체력을 1 깎는 경우와 체력을 i - 2 깎은 상태에서 $P/100$의 확률로 체력을 2 깎는 경우가 존재한다.

 

3. 체력을 i 깎기 위한 타격 횟수의 기댓값을 table[i]라고 하였을 때, 다음과 같은 점화식을 도출할 수 있다.
$table[i] = (table[i-1] + 1) \times (p/100) + (table[i - 2] + 1) \times (1 - p/100) $

 

4. table[0] = 0, table[1] = 1을 초기조건으로 세운 뒤, 페르마의 소정리를 이용하여 요구하는 값을 출력한다.

#include <bits/stdc++.h>
using namespace std;
const long long mod = 998244353;

int N, P;
long long table[200001];

long long cal(long long A, long long B);

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> N >> P;
	table[0] = 0; table[1] = 1;
	for (int i = 2; i <= N; i++) table[i] = (((table[i - 2] + 1) % mod * cal(P, 100)) % mod + ((table[i - 1] + 1) % mod * cal(100 - P, 100)) % mod) % mod;
	cout << table[N];
	return 0;
}

long long cal(long long A, long long B) {
	long long ret = A, bit = 1, x = B;
	while (bit <= mod - 2) {
		if (bit & (mod - 2)) ret = (ret * x) % mod;
		x = (x * x) % mod;
		bit <<= 1;
	}
	return ret;
}

 

 

 

F. Pay or Receive

 

 

요약

 

 

N개의 정점과 M개의 간선을 가지는 가중치 그래프가 주어진다. 임의의 정점 X에서 Y로 향하는 비용을 구하는 Q개의 쿼리를 처리한다.


1. 그래프의 특수성을 파악한다. 두 정점간에 방향이 반대이고, 가중치가 서로 덧셈 항등원인 간선이 주어진다. 즉, A에서 B로 향하는 간선의 가중치가 C일 때, B에서 A로 향하는 가중치는 -C이다.

 

2. 임의의 정점 X와 Y는 다음 세 관계중 하나로 나타낼 수 있다.
- 서로 연결되지 않은 경우

- 서로 연결되어 있고, 사이클이 존재하지 않는 경우

- 서로 연결되어 있고, 사이클이 존재하는 경우

 

3. 첫 번째 경우는 간선을 받으며 두 정점을 유니온 연산하여 유니온 파인드(Union-Find) 기법으로 판별할 수 있다.

 

4. 그래프의 특수성에 의하여 사이클이 존재하지 않는 경로에서 한 노드에서 자신으로 되돌아오는 모든 경로의 비용의 합은 0이다.
X에서 Y로 향하는 비용을 $C(X,Y)$라고 할 때, $C(X,X) = 0$이 항상 성립한다. 그 이유는,  자신으로 되돌아오는 경로는 왔던 길을 돌아가는 방법밖에 존재하지 않는데, A->B와 B->A의 가중치는 항상 0이기 때문이다.
따라서, 임의의 정점 V를 잡았을 때, $C(V,V) = C(V,X) + C(X,V) = 0 = C(V,Y) + C(Y,V)$이고, 이를 응용하여 다음과 같은 관계식을 세울 수 있다.
$C(X,Y) = C(X,V) + C(V,Y) = C(V,Y) - C(V,X)$
따라서 유니온 연산을 통해 형성된 연결 그래프의 대표값을 V로 선정하고 DFS를 통해 비용을 구하여 임의의 정점 X와 Y간의 비용을 계산할 수 있다.

 

5. DFS 과정 중 사이클의 유무를 판별할 수 있다. DFS를 돌며 dist[node]에 가중치를 저장하는데, 이미 dist[node]에 값이 있음에도 다른 값을 넣고자 한다면, node로 향하는 경로가 유일하지 않다는 것을 의미하므로, 그 연결그래프에는 사이클이 존재한다.

 

6. 사이클이 존재하는 그래프에서는 가중치를 무한대로 늘릴 수 있기 때문에 inf를 출력한다. 

#include <bits/stdc++.h>
using namespace std;
const long long INF = 0x3f3f3f3f3f3f3f3fLL;

int N, M, Q;
int parent[100001];
int cycle[100001];
long long dist[100001];
vector <pair<int, long long>> graph[100001];

int Find(int node);
void Union(int A, int B);
void DFS(int node, long long cost);

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> N >> M >> Q;
	for (int i = 1; i <= N; i++) parent[i] = i;
	for (int i = 0; i < M; i++) {
		int A, B;
		long long C;
		cin >> A >> B >> C;
		graph[A].push_back({ B,C });
		graph[B].push_back({ A,-C });
		Union(A, B);
	}
	memset(dist, INF, sizeof(dist));
	for (int i = 1; i <= N; i++) if (Find(i) == i) DFS(i, 0);	
	for (int i = 0; i < Q; i++) {
		int A, B;
		cin >> A >> B;
		if (Find(A) != Find(B)) cout << "nan\n";
		else {
			if (cycle[Find(A)] == 1) cout << "inf\n";
			else cout << dist[B] - dist[A] << '\n';
		}
	}
	return 0;
}

int Find(int node) {
	if (parent[node] == node) return parent[node];
	else return parent[node] = Find(parent[node]);
}

void Union(int A, int B) {
	A = Find(A);
	B = Find(B);
	if (A == B) return;
	if (A > B) parent[A] = B;
	else parent[B] = A;
	return;
}

void DFS(int node, long long cost) {
	if (dist[node] != INF) {
		if (dist[node] != cost) cycle[Find(node)] = 1;
		return;
	}
	dist[node] = cost;
	for (int i = 0; i < graph[node].size(); i++) DFS(graph[node][i].first, cost + graph[node][i].second);
}

 

'Problem Solving > Atcoder' 카테고리의 다른 글

Atcoder Beginner Contest 303  (0) 2023.05.30
Atcoder Beginner Contest 281  (0) 2023.01.12
Comments