Submission #1253148

#TimeUsernameProblemLanguageResultExecution timeMemory
1253148jwvg0425World Map (IOI25_worldmap)C++20
100 / 100
32 ms3556 KiB
#include "worldmap.h"
#include <stdio.h>
#include <vector>
#include <queue>
#include <algorithm>
#include <iostream>
#include <string>
#include <bitset>
#include <map>
#include <set>
#include <tuple>
#include <string.h>
#include <math.h>
#include <random>
#include <functional>
#include <assert.h>
#define all(x) (x).begin(), (x).end()
#define xx first
#define yy second

using namespace std;

template<typename T, typename Pr = less<T>>
using pq = priority_queue<T, vector<T>, Pr>;
using i64 = long long int;
using ii = pair<int, int>;
using ii64 = pair<i64, i64>;

vector<int> edges[45];
int d[45], h[45], subSz[45];
bool perfect[45];

struct RectBoard
{
	RectBoard(int c, int a, int b) : color(c), width(a), height(b), depth(0), coverC(0), special(false) {}

	int color;
	int width, height;
	int depth;
	int coverC = 0;
	bool special;


	void add_child(int x, int y, RectBoard* r)
	{
		childs.emplace_back(ii(x, y), r);
		depth = max(depth, r->depth + 1);
		if (childs.size() == 1)
			coverC = childs[0].yy->coverC + 1;
	}

	vector<pair<ii, RectBoard*>> childs;

	void paint(vector<vector<int>>& res, int x, int y)
	{
		for (int i = x; i < x + width; i++)
			for (int j = y; j < y + height; j++)
				res[i][j] = color;

		for (auto& [p, c] : childs)
			c->paint(res, x + p.xx, y + p.yy);
	}

	int cover()
	{
		// cover 필요 없는 경우
		if (childs.empty())
			return 0;

		if (childs[0].xx.xx != 0 && childs[0].xx.yy != 0)
			return 0;

		auto csz = childs[0].yy->cover() + 1;
		childs[0].xx.xx += 1;
		childs[0].xx.yy += 1;
		width += csz;
		height += csz;

		for (int i = 1; i < childs.size(); i++)
		{
			childs[i].xx.xx += csz;
			childs[i].xx.yy += csz;
		}

		compress(1, 1);

		return csz;
	}

	void compress(int sx, int sy)
	{
		// 특수 모양 제외
		if (special)
			return;

		// 1x1 인 사각형들 전부 분리, 1x1 이상 위치를 기준으로 최대한 껴넣을 수 있는 곳에 껴넣게 변경
		vector<RectBoard*> cells;
		vector<pair<ii, RectBoard*>> nchilds;
		for (auto& [p, c] : childs)
		{
			if (c->width == 1 && c->height == 1)
				cells.push_back(c);
			else
				nchilds.emplace_back(p, c);
		}

		childs = nchilds;

		vector<ii> targets;
		for (int x = sx; x < width - 1;x++)
		{
			for (int y = sy; y < height - 1;y++)
			{
				targets.emplace_back(x, y);
			}
		}

		sort(all(targets), [](ii l, ii r) { return max(l.xx, l.yy) < max(r.xx, r.yy); });

		for (auto& c : cells)
		{
			for (auto& [x, y] : targets)
			{
				bool ok = true;

				for (auto& [p, ch] : childs)
				{
					int lx = p.xx;
					int rx = p.xx + ch->width - 1;
					int ly = p.yy;
					int ry = p.yy + ch->height - 1;

					if (x >= lx && x <= rx && y >= ly && y <= ry)
					{
						ok = false;
						break;
					}

					lx--;
					rx++;
					ly--;
					ry++;

					// 영역에 포함은 아니지만 인접
					if (x >= lx && x <= rx && y >= ly && y <= ry)
					{
						if (x == lx && y == ly)
							continue;

						if (x == lx && y == ry)
							continue;

						if (x == rx && y == ly)
							continue;

						if (x == rx && y == ry)
							continue;

						// 한 칸 여유는 두기
						if (c->width > c->height)
						{
							if (x == rx)
							{
								ok = false;
								break;
							}
						}
						else
						{
							if (y == ry)
							{
								ok = false;
								break;
							}
						}

						// 인접하지만 색상 조건에 문제없는 경우
						if (any_of(all(edges[ch->color]), [x = c->color](int v) { return v == x;}))
							continue;

						ok = false;
						break;
					}
				}

				if (ok)
				{
					add_child(x, y, c);
					break;
				}
			}
		}

		width = 0;
		height = 0;

		for (auto& [p, c] : childs)
		{
			width = max(width, p.xx + c->width + 1);
			height = max(height, p.yy + c->height + 1);
		}
	}

	bool isAdj(ii p)
	{
		for (auto& [cp, v] : childs)
		{
			int lx = cp.xx - 1;
			int rx = cp.xx + v->width;
			int ly = cp.yy - 1;
			int ry = cp.yy + v->height;

			if (p.xx == lx && p.yy == ly)
				continue;

			if (p.xx == lx && p.yy == ry)
				continue;

			if (p.xx == rx && p.yy == ly)
				continue;

			if (p.xx >= lx && p.xx <= rx && p.yy >= ly && p.yy <= ry)
				return true;
		}

		return false;
	}

	vector<ii> getCells()
	{
		vector<ii> res1;

		for (int y = 0; y < height - 1; y++)
		{
			for (int x = y % 2; x < width - 1; x += 2)
			{
				if (isAdj(ii(x, y)))
					continue;

				res1.emplace_back(x, y);
			}
		}

		vector<ii> res2;

		for (int y = 0; y < height - 1; y++)
		{
			for (int x = (y + 1) % 2; x < width - 1; x += 2)
			{
				if (isAdj(ii(x, y)))
					continue;

				res2.emplace_back(x, y);
			}
		}

		if (res1.size() >= res2.size())
			return res1;
		else
			return res2;
	}

	void transpose()
	{
		swap(width, height);

		for (auto& [p, c] : childs)
		{
			swap(p.xx, p.yy);
			c->transpose();
		}
	}

	bool overlap(int lx, int ly, int rx, int ry)
	{
		for (auto& [p, c] : childs)
		{
			int lx2 = p.xx - 1;
			int ly2 = p.yy - 1;
			int rx2 = p.xx + c->width;
			int ry2 = p.yy + c->height;

			int minx = max(lx, lx2);
			int maxx = min(rx, rx2);
			int miny = max(ly, ly2);
			int maxy = min(ry, ry2);

			if (minx <= maxx && miny <= maxy)
				return true;
		}

		return false;
	}
};

void predfs(int root)
{
	subSz[root] = 1;
	int backEdge = 0;
	vector<int> cs;
	for (auto& c : edges[root])
	{
		if (d[c] != -1)
		{
			if (d[c] > d[root])
				backEdge++;
			continue;
		}

		cs.push_back(c);
		d[c] = d[root] + 1;
		predfs(c);
		h[root] = max(h[root], h[c] + 1);
		subSz[root] += subSz[c];
	}

	if (cs.empty())
	{
		perfect[root] = true;
	}
	else if (cs.size() == 1 && perfect[cs[0]] && backEdge == subSz[root] - 2)
	{
		perfect[root] = true;
	}
}

RectBoard* dfs(int root, bool maxFirst)
{
	vector<int> backEdges;
	vector<RectBoard*> childBoards;

	if (maxFirst)
		sort(all(edges[root]), [](int l, int r) { return h[r] < h[l]; });
	else
		sort(all(edges[root]), [](int l, int r) { return h[l] < h[r]; });

	int bc = 0;
	for (auto& c : edges[root])
	{
		if (d[c] < d[root])
			continue;

		// back edge 여부 체크
		if (d[c] != d[root] + 1)
		{
			backEdges.push_back(c);
			bc++;
		}
		else
		{
			auto dfsr = dfs(c, maxFirst);
			if (dfsr->width == 1 && dfsr->height == 1)
			{
				backEdges.push_back(dfsr->color);
				delete dfsr;
			}
			else
			{
				childBoards.push_back(dfsr);
			}
			maxFirst = false;
		}
	}

	// leaf node
	if (childBoards.empty() && backEdges.empty())
		return new RectBoard(root, 1, 1);

	// n = 3 완전 그래프  특수 케이스
	if (subSz[root] == 3 && perfect[root])
	{
		auto res = new RectBoard(root, 2, 3);
		res->add_child(0, 0, new RectBoard(childBoards[0]->color, 1, 1));
		res->add_child(0, 1, new RectBoard(childBoards[0]->childs[0].yy->color, 1, 1));
		res->special = true;
		return res;
	}

	// n = 4 완전 그래프 특수 케이스
	if (subSz[root] == 4 && perfect[root])
	{
		auto res = new RectBoard(root, 3, 4);
		res->add_child(0, 0, childBoards[0]);
		res->childs[0].yy->add_child(1, 1, new RectBoard(root, 1, 1));
		res->childs[0].yy->add_child(1, 2, new RectBoard(res->childs[0].yy->childs[0].yy->color, 1, 1));
		res->special = true;
		return res;
	}

	for (int i = 1; i < childBoards.size(); i++)
	{
		// 왼쪽 위 감싸기
		childBoards[i]->cover();
	}

	// 자식을 한 칸씩 띄고 배치할 수 있을 만큼으로 크기 지정
	int width = 0;
	int height = 0;

	auto res = new RectBoard(root, width, height);

	for (auto& c : childBoards)
	{
		// width를 확장할지 height를 확장할지 정하기
		vector<tuple<int, int, bool>> candidates;
		for (int x = 0; x <= width; x++)
		{
			for (int y = 0; y <= height; y++)
			{
				if (!res->overlap(x, y, x + c->width - 1, y + c->height - 1))
					candidates.emplace_back(x, y, false);

				if (!res->overlap(x - 1, y - 1, x + c->height, y + c->width))
					candidates.emplace_back(x, y, true);
			}
		}

		int x = 0;
		int y = 0;
		bool needTranspose = false;
		int sz = 987654321;

		for (auto& [xi, yi, transpose] : candidates)
		{
			auto wi = transpose ? c->height : c->width;
			auto hi = transpose ? c->width : c->height;
			auto afterw = max(width, xi + wi + 1);
			auto afterh = max(height, yi + hi + 1);

			auto aftersz = max(afterw, afterh);

			if (aftersz < sz)
			{
				sz = aftersz;
				x = xi;
				y = yi;
				needTranspose = transpose;
			}
		}

		if (needTranspose)
			c->transpose();

		res->add_child(x, y, c);

		width = max(width, x + c->width + 1);
		height = max(height, y + c->height + 1);
	}

	res->width = width;
	res->height = height;

	// back edge 칸 계산
	if (!backEdges.empty())
	{
		while (true)
		{
			vector<ii> cells = res->getCells();

			if (cells.size() < backEdges.size())
			{
				if (res->width < res->height)
					res->width += 2;
				else
					res->height += 2;
				continue;
			}

			for (int i = 0; i < backEdges.size(); i++)
				res->add_child(cells[i].xx, cells[i].yy, new RectBoard(backEdges[i], 1, 1));

			break;
		}
	}

	return res;
}

vector<vector<int>> create_map(int N, int M, vector<int> A, vector<int> B) {
	for (int i = 1; i <= N; i++)
		edges[i].clear();

	memset(d, -1, sizeof(d));
	memset(h, 0, sizeof(h));
	memset(subSz, 0, sizeof(subSz));
	memset(perfect, false, sizeof(perfect));

	for (int i = 0; i < M; i++)
	{
		edges[A[i]].push_back(B[i]);
		edges[B[i]].push_back(A[i]);
	}

	d[1] = 0;
	predfs(1);
	auto res = dfs(1, true);

	auto sz = max(res->width, res->height);
	res->width = sz;
	res->height = sz;

	vector<vector<int>> ans(sz, vector<int>(sz));
	res->paint(ans, 0, 0);

	return ans;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...