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=lfloorfrac{l+r}2 floor),把 (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(nmlog 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
", X[i], Y[i]);
return 0;
}