티스토리 뷰

오랜만에 부계 jjang36524로 나가 5솔을 했고, 75점이 올랐다.

A번: 가장 먼저 찾을 수 있는 성질은 연산을 했을 때 abs(a-b)는 짝수라는 것이다. 1번 연산은 a-b를 변화시키지 않고, 2,3번 연산은 a-b를 2k만큼 변화시키기 때문이다. 따라서, a-b가 홀수이면 답은 -1이다. 그 외의 경우는 1번 연산 하나와 2번 혹은 3번 연산 하나만으로 가능한데, a==b인 경우는 1번 연산 하나만 필요하고, a==b==0이면 아무 연산도 필요하지 않다. 이를 구현해주면 된다.

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	int T;
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> T;
	while (T--)
	{
		int a, b;
		cin >> a >> b;
		if (a > b)
			swap(a, b);
		if ((b - a) % 2)
		{
			cout << -1 << '\n';
		}
		else
		{
			cout << (b&&b) + (a != b) << '\n';
		}
	}
}

B번: a를 모두 0과 1로 바꿔주고 시작하자. 0의 개수와 1의 개수의 차가 2 이상이면 불가능하다는 것을 증명할 수 있다.

0의 개수와 1의 개수가 같다면, 010101... 순서와 101010... 순서가 가능하고, 1 차이가 난다면 더 많은 것부터 시작한 뒤 개수가 같을 때처럼 넣어주는 경우 한 가지만 가능하다. 어떤 순서가 있을 때의 정답은 (abs(수열에서 i번째 0 위치-만들어야 하는 i번째 0 위치)의 합+abs(수열에서 i번째 1 위치-만들어야 하는 i번째 1 위치)의 합)/2가 된다.

#include <iostream>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
signed main()
{
	int T;
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> T;
	while (T--)
	{
		int N;
		cin >> N;
		vector<int>x;
		vector<int>y;
		int i;
		for (i = 0; i < N; i++)
		{
			int a;
			cin >> a;
			if (a % 2)
				x.push_back(i);
			else
				y.push_back(i);
		}
		if (x.size() > y.size())
			swap(x, y);
		if (x.size() + 1 < y.size())
		{
			cout << -1 << '\n';
		}
		else
		{
			long long ans = 1LL<<60;
			long long cur = 0;
			for (i = 0; i < x.size(); i++)
			{
				cur += abs(x[i] - (i * 2+1));
			}
			for (i = 0; i < y.size(); i++)
			{
				cur += abs(y[i] - (i * 2));
			}
			ans = cur/2;
			if (x.size() == y.size())
			{
				swap(x, y);
				cur = 0;
				for (i = 0; i < x.size(); i++)
				{
					cur += abs(x[i] - (i * 2 + 1));
				}
				for (i = 0; i < y.size(); i++)
				{
					cur += abs(y[i] - (i * 2));
				}
				ans = min(ans,cur/2);
			}
			cout << ans << '\n';
		}
	}
}

C번: 괄호열을 (가 1, )가 -1인 수열로 바꾼 뒤 이 수열의 prefix sum을 저장해두자. 괄호 문자열은 prefix sum이 k인 곳 바로 뒤에서 시작해서 k인 곳에서 끝나는데, 중간에 prefix sum이 k보다 작아지지 않아야 한다. 이를 이용하면 문제를 풀 수 있다. 특정 높이에서 시작하는 순열의 개수를 구하려면 prefix sum을 돌아보면서 k 이하다가 k 초과로 올라가는 곳이 있으면 카운터를 1 올리고, k 초과에서 k 이하로 내려가면 카운터를 정답에 더하고, 현재 prefix sum이 k 미만이라면 카운터를 초기화해주며 구할 수 있다. 처리해줘야 할 높이는 변곡점의 prefix sum과 변곡점의 prefix sum+1뿐으로, 나머지는 바로 밑에 있는 처리해줘야 하는 높이와 정답이 같다.

#include <iostream>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
vector<int>psum;
signed main()
{
	int N;
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> N;
	psum.push_back(0);
	int i;
	vector<int>pl{ 0,1 };
	for (i = 0; i < N; i++)
	{
		int a;
		cin >> a;
		psum.push_back(psum.back() + a * (i % 2?-1 : 1));
		pl.push_back(psum.back());
		pl.push_back(psum.back() + 1);
	}
	
	sort(pl.begin(), pl.end());
	int ans = 0;
	for (i = 0; i < pl.size()-1; i++)
	{
		int an = 0;
		int cu = 0;
		int j;
		for (j = 1; j < psum.size(); j++)
		{
			if (psum[j - 1] <= pl[i] && psum[j] > pl[i])
			{
				cu++;
			}
			if (psum[j] <= pl[i] && psum[j-1] > pl[i])
			{
				an += cu;
			}
			if (psum[j] < pl[i])
			{
				cu = 0;
			}
		}
		ans += an*(pl[i+1]-pl[i]);
	}
	cout << ans;
}

D번: 이 문제에서 가장 중요한 관찰은 i and j + i or j는 i+j라는 것이다. 이를 이용하면 a[1]+a[2], a[2]+a[3],a[3]+a[1]을 찾아서 첫 3개 원소를 구하고, 나머지는 a[1]+a[i]를 사용해서 구할 수 있다.

#include <iostream>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
vector<int>psum;
int res[10100];
signed main()
{
	int N,K;
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> N>>K;
	int i;
	int arr[5][5];
	for (i = 1; i < 3; i++)
	{
		int j;
		for (j = i + 1; j <= 3; j++)
		{
			arr[i][j] = 0;
			cout << "and " << i << ' ' << j << '\n';
			cout.flush();
			int a;
			cin >> a;
			arr[i][j] += a;
			cout << "or " << i << ' ' << j << '\n';
			cout.flush();
			cin >> a;
			arr[i][j] += a;
		}
	}
	int ans = (arr[1][2] + arr[2][3] + arr[1][3]) / 2;
	res[1] = ans - arr[2][3];
	res[2] = ans - arr[1][3];
	res[3] = ans - arr[1][2];
	for (i = 4; i <= N; i++)
	{
		cout << "and " << i << ' ' << 1 << '\n';
		cout.flush();
		int a;
		cin >> a;
		res[i] += a;
		cout << "or " << i << ' ' << 1 << '\n';
		cout.flush();
		cin >> a;
		res[i] += a;
		res[i] -= res[1];
	}
	sort(res + 1, res + N + 1);
	cout << "finish " << res[K] << '\n';
	cout.flush();
}

E번:a[i]-b[i]의 prefix sum을 저장해두면 이 값을 기반으로 여러 성질을 찾을 수 있다. 우선 안 되는 경우는 l-1까지의 부분합이 r까지의 부분합과 다른 경우, l-1~r까지의 부분합의 최솟값이 r까지의 부분합과 다른 경우이다. 나머지 경우에는 l-1~r까지의 부분합의 최댓값-r까지의 부분합이 정답이고, 이는 세그먼트 트리로 구할 수 있다.

#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
class stree
{
private:
	int tree[1 << 18];
	int N, treeN;
public:
	stree()
	{
		memset(tree, 0, sizeof(tree));
	}
	int gettreeN()
	{
		return treeN;
	}

	void setNtreeN(int newN)
	{
		N = newN;
		for (treeN = 1; treeN < N; treeN *= 2);
	}
	void set(vector<int>val, int op)
	{
		int i;
		for (i = 0; i < val.size(); i++)
		{
			tree[i + treeN] = val[i];
		}
		for (i = treeN - 1; i; i--)
		{
			if (op)
			{
				tree[i] = max(tree[i * 2], tree[i * 2 + 1]);
			}
			else
			{
				tree[i] = min(tree[i * 2], tree[i * 2 + 1]);
			}
		}
	}
	int res(int qstart, int qend, int start, int end, int ind, int op)
	{
		if (start > qend || qstart > end)
			return op ? (-(1LL<<60)) : 1LL << 60;
		if (start >= qstart && end <= qend)
			return tree[ind];
		if (op)
		{
			return max(res(qstart, qend, start, (start + end) / 2, ind * 2, op), res(qstart, qend, (start + end) / 2 + 1, end, ind * 2 + 1, op));
		}
		else
		{
			return min(res(qstart, qend, start, (start + end) / 2, ind * 2, op), res(qstart, qend, (start + end) / 2 + 1, end, ind * 2 + 1, op));
		}
	}
}x,y;
int arr[100100];
signed main()
{
	int N, Q;
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> N >> Q;
	int i;
	for (i = 0; i < N; i++)
	{
		cin >> arr[i];
	}
	x.setNtreeN(N + 1);
	y.setNtreeN(N + 1);
	vector<int>psum;
	psum.push_back(0);
	for (i = 0; i < N; i++)
	{
		int a;
		cin >> a;
		psum.push_back(psum.back() - arr[i] + a);
	}
	x.set(psum,1);
	y.set(psum, 0);
	int treeN = x.gettreeN();
	while (Q--)
	{
		int a, b;
		cin >> a >> b;
		a--;
		if (psum[a] != psum[b])
		{
			cout << -1 << '\n';
		}
		else
		{
			int ma = x.res(a, b, 0, treeN - 1, 1, 1);
			int mi = y.res(a, b, 0, treeN - 1, 1, 0);
			if (mi < psum[a])
				cout << -1 << '\n';
			else
				cout << ma - psum[a] << '\n';
		}
	}
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함