有(n+1)个栈,一开始前(n)个栈中每个栈各有(m)个数。每种数的出现次数为(n)。
每次将一个栈的栈顶取出丢到另一个栈的栈顶。要求操作之后栈大小不超过(m)。
要求每个栈中元素相同。
(nle 50,mle 400)
操作次数限制(820000)。
构造白痴表示连40分都不会。
讲正解:
考虑(n=2)的情况,分两步:将每个栈调整为上面白下面黑,然后再随便搞搞。
设栈(A)黑色有(x)个。如此操作:
- 取(B)中(x)个数丢入(C)中。
- 取(A)中的所有数,如果是黑色丢入(B)中,否则丢入(C)中。
- 将(B)中(x)个数(黑色)丢入(A)中,(C)中(m-x)个数丢入(A)中。
可以发现,这样之后(A)被成功调整,而其它的没有变化。
考虑(n>2)。可以分治做,黑白染色后做上述操作。
搞完之后需要把栈变成全黑或全白。可以这样搞:随便取出两个栈,记(x,y)分别为它们的黑色出现次数。如果(x+yge m),将(A)和(B)上的白色都丢入(C)中,把(B)中的黑色丢入(A)中,再将白色放回来;如果(x+y<m),把(A)全部丢入(C)中,把(B)的白色丢入(A)中,把(C)中的黑色丢到(B)中,剩下的白色丢到(A,B)中。
发现复杂度是(O(nmlg 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
",oc);
for (int i=1;i<=oc;++i)
printf("%d %d
",o[i].first,o[i].second);
return 0;
}