NOIP2020T3题解

【解题思路】
1.40分做法
我们可以用2m次操作将两个球A,B交换,做法:记空柱子为C,假设A球上面的球的个数大于B的(否则交换AB),把A球的上面的球移动到空柱子C上,然后把B球上面的球移动到A上面,然后把A上面的球加上A球移动到B柱子)最后把空柱子C的球移动到A柱子。
我们把m
n个球通过这种交换操作归位,时间复杂度O(nm^2)可以通过40%的数据。
2.70分做法
我们一个颜色一个颜色的处理,假如我们现在处理到颜色i,考虑一个柱子一个柱子的处理,假设颜色i的球是红球,不是颜色i的球是黑球,如果柱子A有红球(不一定在上面)我们可以通过如下操作使柱子A的红球全都移动到A柱子的上方:记辅助柱子为B,记空柱子为C,首先算一下柱子A有多少个红球,记为k个,把柱子B上方k个球移动到空柱子C上。对于A柱子,可以从顶部一个一个移动,如果是红球,移动到B柱子上,如果是黑球,移动到C上。这时候A空了,C上面都是A柱子上的黑球,下面k个是b柱子上面的球,而b柱子上面是k个红球。我们还原C柱子,我们把C上面的黑球移动到A,然后把B上面的k个红球移动到A最后把C柱子下面的k个球给B。这样我们就把A柱子上的红球移动到上方,而B柱子没有改变,这样一次要用m2+k2次操作。最后再把每个柱子上方的红球移动到某一个柱子上,然后移动黑球,留出一个空柱子,这样一个颜色就完成了。计算次数,因为k的总和是m,所以对于每个柱子做一遍要用n*(m2)+m2次,而对于每种颜色都做一次,要用(1+2+…n)(m2)+mn2大约是O(n^2m)的时间复杂度,在n=50,m=300大概是75万次,可以拿到70分,而在m=400时需要100万次,还是不能通过本题。
3.100分做法
我们可以发现,在70分部分分中,将柱子C还原是一个很消耗次数的操作,我们考虑为什么要还原,因为柱子C下面是原来B上方的k个球,里面可能有红球。考虑如果B是一个全黑球的柱子,在还原之前,C就是全黑球的柱子,而B上方是k个红球,A,B和C正好是还原之后的C,A和B柱子,这样我们就省下了还原的操作,操作顿时少了一半。最后只剩下一个问题,如何构造出一个全是黑球的柱子?考虑两个柱子,因为红球的个数一定小于等于m,所以黑球的个数一定大于等于m于是我们可以用70分部分分的操作把两个柱子的红球全移动到它们的上方,然后把两个柱子红球移动到空柱子,再把黑球移动到一个柱子上,最后把空柱子上的红球移动到另一个柱子上,这样就能用4m次构造出一个全是黑球的柱子。计算次数,因为不用还原,所以复杂度瓶颈nnm次操作变成了nnm/2次操作,再加上一开始和最后的处理大概nm*10次操作,总共约600000次操作,可以通过本题。
【参考代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 55
#define M 410
int a[N][M], c[N];
struct {
    
    
	int x, y;
}to[825000];
int ans = 0, n, m;
void mv(int x, int y) {
    
    
	a[y][++c[y]] = a[x][c[x]];
	a[x][c[x]--] = 0;
	to[++ans].x = x, to[ans].y = y;
}
void solve(int l, int r) {
    
    
	if(l == r) return;
	int mid = (l + r) / 2;
	int i = l, j = mid + 1, k;
	while(i <= mid && j <= r) {
    
    
		int s = 0;
		for(k = 1; k <= m; k++) s += (a[i][k] <= mid);
		for(k = 1; k <= m; k++) s += (a[j][k] <= mid);
		int x = i, y = j;
		if(s > m) {
    
    
			int t = 0;
			for(k = 1; k <= m; k++) t += (a[i][k] <= mid);
			for(k = 1; k <= t; k++) mv(j, n + 1);
			for(k = m; k; k--) if(a[i][k] > mid) mv(i, n + 1); else mv(i, j);
			for(k = 1; k <= t; k++) mv(j, i);
			for(k = t + 1; k <= m; k++) mv(n + 1, i);
			for(k = t; k; k--) mv(n + 1, j);
			
			int t0 = 0;
			for(k = 1; k <= m; k++) t0 += (a[j][k] <= mid);
			for(k = 1; k <= t0; k++) mv(i, n + 1);
			for(k = m; k; k--) if(a[j][k] > mid) mv(j, n + 1); else mv(j, i);
			for(k = 1; k <= t0; k++) mv(i, j);
			for(k = t0 + 1; k <= m; k++) mv(n + 1, j);
			for(k = t0; k; k--) mv(n + 1, i);
			
			for(k = t + 1; k <= m; k++) mv(i, n + 1);
			for(k = t0 + 1; k <= m; k++) mv(j, n + 1);
			for(k = t + 1; k <= m; k++) mv(j, i);
			while(c[n + 1]) mv(n + 1, j);
			i++;
		}
		else {
    
    
			int t = 0;
			for(k = 1; k <= m; k++) t += (a[i][k] > mid);
			for(k = 1; k <= t; k++) mv(j, n + 1);
			for(k = m; k; k--) if(a[i][k] <= mid) mv(i, n + 1); else mv(i, j);
			for(k = 1; k <= t; k++) mv(j, i);
			for(k = t + 1; k <= m; k++) mv(n + 1, i);
			for(k = t; k; k--) mv(n + 1, j);
			
			int t0 = 0;
			for(k = 1; k <= m; k++) t0 += (a[j][k] > mid);
			for(k = 1; k <= t0; k++) mv(i, n + 1);
			for(k = m; k; k--) if(a[j][k] <= mid) mv(j, n + 1); else mv(j, i);
			for(k = 1; k <= t0; k++) mv(i, j);
			for(k = t0 + 1; k <= m; k++) mv(n + 1, j);
			for(k = t0; k; k--) mv(n + 1, i);
			
			for(k = t + 1; k <= m; k++) mv(i, n + 1);
			for(k = t0 + 1; k <= m; k++) mv(j, n + 1);
			for(k = t0 + 1; k <= m; k++) mv(i, j);
			while(c[n + 1]) mv(n + 1, i);
			j++;
		}
	}
	solve(l, mid), solve(mid + 1, r);
}
int main() {
    
    
	int i, j, k, l;
	scanf("%d%d", &n, &m);
	for(i = 1; i <= n; i++) 
		for(j = 1; j <= m; j++) scanf("%d", &a[i][j]);
	for(i = 1; i <= n; i++) c[i] = m;
	solve(1, n);
	printf("%d\n", ans);
	for(i = 1; i <= ans; i++) printf("%d %d\n", to[i].x, to[i].y);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yueyuedog/article/details/112322100