NOIP2020 T3移球游戏

\(n+1\)个栈,一开始前\(n\)个栈中每个栈各有\(m\)个数。每种数的出现次数为\(n\)

每次将一个栈的栈顶取出丢到另一个栈的栈顶。要求操作之后栈大小不超过\(m\)

要求每个栈中元素相同。

\(n\le 50,m\le 400\)

操作次数限制\(820000\)


构造白痴表示连40分都不会。

讲正解:

考虑\(n=2\)的情况,分两步:将每个栈调整为上面白下面黑,然后再随便搞搞。

设栈\(A\)黑色有\(x\)个。如此操作:

  1. \(B\)\(x\)个数丢入\(C\)中。
  2. \(A\)中的所有数,如果是黑色丢入\(B\)中,否则丢入\(C\)中。
  3. \(B\)\(x\)个数(黑色)丢入\(A\)中,\(C\)\(m-x\)个数丢入\(A\)中。

可以发现,这样之后\(A\)被成功调整,而其它的没有变化。

考虑\(n>2\)。可以分治做,黑白染色后做上述操作。

搞完之后需要把栈变成全黑或全白。可以这样搞:随便取出两个栈,记\(x,y\)分别为它们的黑色出现次数。如果\(x+y\ge m\),将\(A\)\(B\)上的白色都丢入\(C\)中,把\(B\)中的黑色丢入\(A\)中,再将白色放回来;如果\(x+y<m\),把\(A\)全部丢入\(C\)中,把\(B\)的白色丢入\(A\)中,把\(C\)中的黑色丢到\(B\)中,剩下的白色丢到\(A,B\)中。

发现复杂度是\(O(nm\lg n)\)的,可以过。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cassert>
#define N 55
#define M 405
int n,m;
int a[N][M],c[N];
pair<int,int> o[1000000];
int oc;
void opt(int x,int y){
	o[++oc]={x,y};	
	assert(c[y]<m);
	assert(c[x]>0);
	a[y][++c[y]]=a[x][c[x]--];
}
int cnt(int i,int mid){
	assert(c[i]==m);
	int s=0;
	for (int j=1;j<=m;++j)
		s+=(a[i][j]<=mid);
	return s;
}
void divide(vector<int> p,int l,int r){
	if (p.size()==1)
		return;
	int mid=l+r>>1;
	for (int ii=0;ii<p.size();++ii){
		int i=p[ii],t=(i==1?n:1),x=cnt(i,mid);
		for (int j=1;j<=x;++j)
			opt(t,n+1);
		for (int j=m;j>=1;--j)
			if (a[i][j]<=mid)
				opt(i,t);
			else
				opt(i,n+1);
		for (int j=1;j<=x;++j)
			opt(t,i);
		for (int j=1;j<=m-x;++j)
			opt(n+1,i);
		for (int j=1;j<=x;++j)
			opt(n+1,t);
	}
	vector<int> lp,rp;
	lp.clear(),rp.clear();
	for (int ii=0;ii<p.size();++ii){
		int i=p[ii],x=cnt(i,mid);
		if (x==m)
			lp.push_back(i);
		else if (x==0)
			rp.push_back(i);
		else{
			int t=p[ii+1],y=cnt(t,mid);
			if (x+y>=m){
				for (int j=1;j<=m-x;++j)
					opt(i,n+1);
				for (int j=1;j<=m-y;++j)
					opt(t,n+1);
				for (int j=1;j<=m-x;++j)
					opt(t,i);
				for (int j=1;j<=m-y+m-x;++j)
					opt(n+1,t);
				lp.push_back(i);
			}
			else{
				for (int j=1;j<=m;++j)
					opt(i,n+1);
				for (int j=1;j<=m-y;++j)
					opt(t,i);
				for (int j=1;j<=x;++j)
					opt(n+1,t);
				for (int j=1;j<=y;++j)
					opt(n+1,i);
				for (int j=1;j<=m-x-y;++j)
					opt(n+1,t);
				rp.push_back(i);
			}
		}
	}
//	for (int i=0;i<lp.size();++i)
//		for (int j=1;j<=m;++j)
//			assert(a[lp[i]][j]<=mid);
	divide(lp,l,mid);
	divide(rp,mid+1,r);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i){
		for (int j=1;j<=m;++j)
			scanf("%d",&a[i][j]);
		c[i]=m;
	}
	vector<int> p;
	p.clear();
	for (int i=1;i<=n;++i)
		p.push_back(i);
	divide(p,1,n);
	printf("%d\n",oc);
	for (int i=1;i<=oc;++i)
		printf("%d %d\n",o[i].first,o[i].second);
	return 0;
}
posted @ 2020-12-16 22:24  jz_597  阅读(132)  评论(0编辑  收藏  举报