NOIP2020 移球游戏

题目链接

Solution

考虑一个基本操作,如果有两个柱子 \(x,y\) 是满的,\(z\) 是空的,这 \(2m\) 个球中有 \(m\) 个关键球,要把所有的关键球移到同一根柱子上。

\(x\) 柱上有 \(a\) 个关键球,操作如下:

(1)把 \(y\) 柱顶部的 \(a\) 个球移到 \(z\) 柱。

(2)把 \(x\) 柱上的球依次移走,如果是关键球就移到 \(y\) 柱,否则移到 \(z\) 柱。

(3)把 \(z\) 柱顶部的 \(m-a\) 个球移回 \(x\) 柱,再把 \(y\) 柱顶部的 \(a\) 个球移回 \(x\) 柱。

(4)把 \(x\) 柱顶部的 \(a\) 个关键球移到 \(z\) 柱。

(5)把 \(y\) 柱上的球依次移走,如果是关键球就移到 \(z\) 柱,否则移到 \(x\) 柱。

这样结束之后,所有的关键球都到了 \(z\) 柱上。

这样 \(n=2\) 就做完了,\(O(m)\)

回到原问题,考虑分治:定义分治函数 \(solve(l,r)\),每次定 \(mid=\lfloor\frac{l+r}2\rfloor\),把 \(\le mid\)\(>mid\) 的球分开,然后进行 \(solve(l,mid)\)\(solve(mid+1,r)\)

如何分开,可以取出这 \(r-l+1\) 根柱子,每次找两根柱子,如果这两根柱子中 \(\le mid\) 的球比 \(>mid\) 的球多,就选 \(m\)\(\le mid\) 的球作为关键球,否则选 \(m\)\(>mid\) 的球作为关键球,进行一轮以上基本操作,这样就制造出了一个满足所有球都在 \([l,mid]\) 内或所有球都在 \([mid+1,r]\) 内的柱子。

经过 \(r-l\) 轮,所有 \(r-l+1\) 根柱子都满足这个条件,可以递归下去。

操作次数 \(O(nm\log n)\)

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

const int N = 55, M = 405, L = 888888;

int n, m, tot, X[L], Y[L], sze[N], a[N][M], em;
bool isx[M], isy[M], vis[N];

void record(int x, int y)
{
	a[y][++sze[y]] = a[x][sze[x]]; sze[x]--;
	X[++tot] = x; Y[tot] = y;
}

int mer(int x, int y, int mid)
{
	int cntl = 0, cntr = 0;
	memset(isx, 0, sizeof(isx)); memset(isy, 0, sizeof(isy));
	for (int i = 1; i <= m; i++) isx[i] = a[x][i] <= mid,
		isy[i] = a[y][i] <= mid, cntl += isx[i], cntr += isy[i];
	if (cntl + cntr > m)
	{
		cntl = m - cntl; cntr = m - cntr;
		for (int i = 1; i <= m; i++) isx[i] ^= 1, isy[i] ^= 1;
	}
	for (int i = 1; i <= m; i++) if (!isx[i] && cntl + cntr < m)
		cntl++, isx[i] = 1;
	for (int i = 1; i <= cntl; i++) record(y, em);
	for (int i = m; i >= 1; i--) record(x, isx[i] ? y : em);
	for (int i = 1; i <= m - cntl; i++) record(em, x);
	for (int i = 1; i <= cntl; i++) record(y, x);
	for (int i = 1; i <= cntl; i++) record(em, y);
	for (int i = 1; i <= cntl; i++) record(x, em);
	for (int i = m; i >= 1; i--) record(y, isy[i] ? em : x);
	int res = em;
	return em = y, res;
}

void solve(int l, int r)
{
	if (l == r) return;
	int mid = l + r >> 1;
	std::vector<int> orz;
	for (int i = 1; i <= n + 1; i++)
		if (sze[i] && l <= a[i][1] && a[i][1] <= r)
			orz.push_back(i);
	for (int i = 0; i + 1 < orz.size(); i++)
		orz[i + 1] = mer(orz[i], orz[i + 1], mid);
	solve(l, mid); solve(mid + 1, r);
}

int main()
{
	#ifdef ONLINE_JUDGE
		freopen("ball.in", "r", stdin);
		freopen("ball.out", "w", stdout);
	#endif
	
	read(n); read(m); em = n + 1;
	for (int i = 1; i <= n; i++)
	{
		sze[i] = m;
		for (int j = 1; j <= m; j++) read(a[i][j]);
	}
	solve(1, n);
	std::cout << tot << std::endl;
	for (int i = 1; i <= tot; i++) printf("%d %d\n", X[i], Y[i]);
	return 0;
}
posted @ 2020-12-19 16:35  epic01  阅读(513)  评论(0编辑  收藏  举报