• 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=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;
    }
    
  • 相关阅读:
    [LeetCode] Maximum Depth of Binary Tree
    [LeetCode] Binary Tree Level Order Traversal II
    阿里第一天——maven学习
    微博用户行为分析
    对节目微博进行强过滤之后的处理
    关于推荐和机器学习的几个网站
    大论文微博个性化
    新浪微博用户分析
    位运算符规律小结
    字符串类常见面试大题
  • 原文地址:https://www.cnblogs.com/xyz32768/p/14159770.html
Copyright © 2020-2023  润新知