티스토리 뷰

카테고리 없음

Codeforces Round #754 (Div. 2)

jjang36524 2021. 11. 14. 10:17

A,B,C,D,E를 50분 내에 풀어서 2등을 하였다.

0:00~0:01

A번을 생각했다. 수 하나를 늘리고 다른 수를 줄이는 연산으로는, 합이 원래 a,b,c와 같은 모든 a,b,c의 쌍을 만들 수 있다. 만약 a+b+c가 3의 배수이면 a+c=2b, 아니면 a+c=2b±1의 꼴로 만들 수 있다. a+c=2b가 가능하면 답은 0, 아니면 답은 1이다. 구현은 1분만에 끝낼 수 있었다.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
signed main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		if ((a + b + c) % 3 == 0)
			cout << 0 << '\n';
		else
			cout << 1 << '\n';
	}
}

0:01~0:05

A번 AC를 확인한 후, B번으로 넘어갔다. 이 문제에서 중요한 관찰은, 정렬됐을 때 0이여야 하는데 현재 1인 위치와, 정렬됐을 때 1인데 현재 0인 위치의 개수가 같다는 것이다. 이 모든 위치들을 뒤집어주면 1과 0이 바뀌면서 정렬이 된다. 이를 통하면 최대 1번에 정렬이 가능하며, 이미 정렬되어 있는 경우 0을 출력하는 것을 잊으면 안 된다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
signed main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int N;
		cin >> N;
		string s;
		cin >> s;
		int i;
		vector<int>a;
		int c = 0;
		for (i = 0; i < N; i++)
		{
			c += s[i] == '0';
		}
		for (i = 0; i < N; i++)
		{
			if ((s[i] == '0') != (i < c))
			{
				a.push_back(i);
			}
		}
		if (a.size())
		{
			cout << 1 << '\n';
			cout << a.size() << ' ';
			for (i = 0; i < a.size(); i++)
				cout << a[i]+1 << ' ';
			cout << '\n';
		}
		else
			cout <<  0 << '\n';
	}
}

0:05~0:09

C번을 생각하기 시작했다. a들의 위치를 모두 저장한 뒤, 바로 전 a와 지금 탐색하는 a 사이에 b,c가 각각 2개 미만이면 최솟값에 저장하고, 아니면 넘기는 풀이를 생각했다. 구현했더니 wa를 받았다.

0:09~0:14

이 코드의 반례는 abbacca이다. 바로 전 a만 봐서는 답을 찾을 수 없지만, 길이 7의 답이 있다. 이를 해결하기 위해, 바로 전 a뿐만 아니라 두 번째 전 a까지 체크하도록 코드를 짰고, ac를 받을 수 있었다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
signed main()
{
	int T;
	cin >> T;
	while (T--)
	{
		int N;
		cin >> N;
		string s;
		cin >> s;
		int i;
		int befa = -79797979;//I am 79brue fan
		int ans = 79797979;
		int b = 0;
		int c = 0;
		vector<int>ap;
		for (i = 0; i < s.size(); i++)
		{
			if (s[i] == 'a')
			{
				ap.push_back(i);
			}
		}
		for (i = 1; i < ap.size(); i++)
		{
			int a = 1;
			int b = 0;
			int c = 0;
			int j;
			for (j = i-1; j >= max(0, i - 2); j--)
			{
				int k;
				for (k = ap[j + 1]-1; k >= ap[j]; k--)
				{
					if (s[k] == 'a')
						a++;
					else if (s[k] == 'b')
						b++;
					else
						c++;
				}
				if (a > b && a > c)
				{
					ans = min(ans, ap[i] - ap[j] + 1);
				}
			}
		}
		if (ans > N)
			cout << -1 << '\n';
		else
			cout << ans << '\n';
	}
}

0:14~0:25

이제 D번 문제를 볼 수 있었다. 맨 처음에는 기본 O(N)이 필요한 tree dp를 모든 정점에 대해 해야 한다고 생각해서 어려워 보였다. 하지만 중요한 관찰을 두 개 하고 나니, 문제를 쉽게 풀 수 있었다. 관찰 1. 엣지를 타고 다른 노드로 이동할 필요충분조건은 시작 노드와 도착 노드의 2진법 자리수가 같다는 것이다. 증명: 자릿수가 다르면 더 큰 수의 최고 자릿수는 xor할 때 항상 1이고, 이러면 시작과 도착 중 더 작은 수보다 크다. 자릿수가 같으면 xor의 최고 자릿수는 0이 되고, 이러면 둘 중 더 작은 수보다 작다. 관찰 2: 어떤 간선도 사용하지 못하도록 정점을 배치할 수 있다. 트리에서 임의의 정점을 루트로 잡고 depth가 홀수인 것과 짝수인 것을 나누자. 그러면 depth가 홀수인 것에 몇몇 자릿수의 정점을 배정하고, depth가 짝수인 것에 나머지를 배정할 수 있다. 이는 자릿수가 큰 것(특정 자릿수의 가짓수가 많은 것이 아님에 주의하자)부터 그리디하게 배정할 수 있으면 배정하는 방식으로 해줄 수 있다. 그러면 어떤 정점에 돌을 놓아도 이동을 하지 못하고 선이 이기게 된다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
vector<int>link[200100];
int arr[200100];
int c = 0;
void dfs(int n, int p, int o)
{
	c += o == 1;
	arr[n] = o;
	int i;
	for (i = 0; i < link[n].size(); i++)
	{
		if (link[n][i] != p)
			dfs(link[n][i], n, 3 - o);
	}
}
signed main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	int T;
	cin >> T;
	while (T--)
	{
		c = 0;
		int N;
		cin >> N;
		int i;
		for (i = 1; i <= N; i++)
			link[i].clear();
		for (i = 1; i < N; i++)
		{
			int a, b;
			cin >> a >> b;
			link[a].push_back(b);
			link[b].push_back(a);
		}
		dfs(1, 0, 1);
		vector<vector<int>>r;
		vector<int>to;
		vector<int>oo;
		for (i = 1; i <= N; i*=2)
		{
			vector<int>t;
			int j;
			for (j = i; j < min(2 * i, N + 1); j++)
			{
				t.push_back(j);
			}
			r.push_back(t);
		}
		for (i = r.size() - 1; i >= 0; i--)
		{
			if (c >= r[i].size())
			{
				c -= r[i].size();
				int j;
				for (j = 0; j < r[i].size(); j++)
				{
					oo.push_back(r[i][j]);
				}
			}
			else
			{
				int j;
				for (j = 0; j < r[i].size(); j++)
				{
					to.push_back(r[i][j]);
				}
			}
		}
		for (i = 1; i <= N; i++)
		{
			if (arr[i] == 1)
			{
				cout << oo.back()<<' ';
				oo.pop_back();
			}
			else
			{
				cout << to.back() << ' ';
				to.pop_back();
			}
		}
		cout << '\n';
	}
}

0:25~0:50

E번을 생각했다. E번에서 중요한 값은 B-A이므로 이를 C라고 정의한다. 관찰 1. 만약 C1이 정해져 있다면 어떻게 할 수 있을까? 답: C를 순서대로 탐색하면서, Ci가 0이 되도록 연산을 해 주고, 이를 다른 노드들에게 전파해주는 것이다. 일단 B1이 0이라고 가정한 뒤 값을 구해보자. 관찰 2. C1이 1 올라갈 때, 나머지 원소들은 어떻게 변할까? 우선 1번에는 decrease 연산을 1번 더 해 줘야 하고, 나머지 모든 원소에는 increase 연산을 한 번 더 해줘야 한다. 그 다음 2번이 1 커졌으므로 2를 제외한 2의 배수는 decrease 연산을 한 번 더 해야 한다. 이런 식으로 하면 increase를 한 번 더 하는 수, decrease를 한 번 더 하는 수, 그대로인 수 3가지로 나눌 수 있다. 그대로인 수는 신경을 쓰지 말고, increase를 한 번 더하는 수는 현재 Ci이 음수면 정답이 1 감소하고, Ci가 0 이상이면 정답이 1 증가한다. decrase를 한 번 더 하는 수는 Ci가 양수여야 1 감소하고, 아니면 답이 1 증가한다. 이런 식으로 현재 C1이 i 올라갔을 때 답이 얼마나 변하는지 계산을 해주면 문제를 풀 수 있다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
#define int long long
int a[200100];
int b[200100];
int rop[200100];
vector<int>op[3];
int valc[3][2][2000100];
signed main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	int N;
	cin >> N;
	int i;
	rop[1] = 1;
	for (i = 1; i <= N; i++)
	{
		int j;
		for (j = i * 2; j <= N; j += i)
		{
			rop[j] -= rop[i];
		}
	}
	for (i = 1; i <= N; i++)
	{
		cin >> a[i];
	}
	for (i = 1; i <= N; i++)
	{
		cin >> b[i];
		if (b[i] == -1)
			b[i] = 0;
		b[i] = b[i] - a[i];
	}
	int ans = 0;
	for (i = 1; i <= N; i++)
	{
		ans += abs(b[i]);
		op[rop[i] + 1].push_back(b[i]);
		int j;
		for (j = i * 2; j <= N; j += i)
		{
			b[j] -= b[i];
		}
	}
	sort(op[0].begin(), op[0].end());
	sort(op[2].begin(), op[2].end());
	int c = 0;
	int k;
	for (k = 1; k <= 1000000; k++)
	{
		int r = lower_bound(op[0].begin(), op[0].end(), k) - op[0].begin();
		c -= ((int)op[0].size() - r) - r;
		valc[0][1][k] = c;
	}
	c = 0;
	for (k = 1; k <= 1000000; k++)
	{
		int r = lower_bound(op[2].begin(), op[2].end(), -k+1) - op[2].begin();
		c -= r-((int)op[2].size() - r);
		valc[2][1][k] = c;
	}
	int Q;
	cin >> Q;
	while (Q--)
	{
		int a;
		cin >> a;
		int curans = ans;
		curans += valc[0][1][a];
		curans += valc[2][1][a];
		cout << curans << '\n';
	}
}

그 뒤

순위를 확인해 봤는데 1등이여서 기분 좋게 F번을 생각했다. 20분 정도 F번을 생각했는데 아무 것도 생각이 안나서 순위를 다시 봤더니 2등이 되어 있었다. C번을 한번 틀린 것 때문이다 ㅠㅠ. 그 뒤로 10분 더 F를 생각했는데 아무 생각이 안 나서 잤다. 자고 일어났더니 겉보기에는 2339인데 아직 배치고사가 안 끝나서 속은 2489인 이상한 codinglunch가 탄생했다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함