suxxsfe

一言(ヒトコト)

P7115 移球游戏

https://www.luogu.com.cn/problem/P7115

考场上做出了70分做法

先考虑能把一个柱子上的球做什么样的操作,有一种想法是把某一种颜色的球全部放到顶部
具体做法大概是这样,先选择任意一个满栈 \(O\),和一个空栈 \(E\),操作:

  • 记这个要操作的栈为 \(X\),设 \(X\) 中有 \(num\) 个这种颜色的球
  • \(H\) 中最上面的 \(num\) 个放入 \(E\)
  • 对于 \(X\),把所有是这种颜色的球放入 \(O\) 中,不是的放入 \(E\) 中。此时 \(X\) 空,\(E,O\) 均已满
  • \(E\) 中最上面的 \(m-num\) 个放回 \(X\) 中,再把 \(O\) 中最上面的 \(num\) 个放回 \(X\)。此时对于 \(X\) 的操作已完成
  • \(E\) 中剩下的 \(num\) 个放回 \(O\),此时 \(O\) 已经恢复原状,\(E\)

做这样一次操作的操作次数是 \(2m+2num\)

由此,一种比较显然的做法就是,枚举每种颜色,把每个混乱(就是不是单色)的柱子中的这种颜色都放到顶部,然后再把他们全都提取到空栈中,就又多了一个不混乱的柱子
然后继续枚举下一个颜色,直到所有柱子都不混乱
在卡一卡操作次数的常数,计算的可以过 70 分(实际上ccf数据过了80分)

对于正解,采用分治的方法
对于颜色区间 \([l,r]\),把所有 \([l,mid]\) 的颜色视为同一种颜色,\([mid+1,r]\) 也视为同一种
然后把 \([l,mid]\) 的提升到对应柱子的顶部,然后对于每两个柱子,我们总是能令其中的一个中的所有颜色都变成 \([l,mid]\)\([mid+1,r]\)
把每个柱子都选择另一个柱子来这样做就可以了
这样,有 \(mid-l+1\) 个柱子变成了颜色 \([l,mid]\),另外 \(r-mid\) 个变成了 \([mid+1,r]\),再进行递归即可
\(l=r\) 时,也就完成了操作,当前柱子上的颜色统一了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define reg register
inline int read(){
	register int x=0;register int y=1;
	register char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
	return y?x:-x;
}
int n,m;
int stack[55][455],top[55];
int A[820006],B[820006],k;
inline void putt(int a,int b){
//		printf("%d %d\n",a,b);
	A[++k]=a;B[k]=b;
	stack[b][++top[b]]=stack[a][top[a]];
	top[a]--;
}
inline int cnt(int i,int p){
	int num=0;
	for(reg int o=1;o<=m;o++) num+=(stack[i][o]<=p);
	return num;
}
void work(int l,int r,int col[]){
	if(l==r) return;
	int mid=(l+r)>>1;
	for(reg int i=1;i<=r-l+1;i++){
		int other=i+1;
		if(other==r-l+2) other=1;
		int backup=i;
		i=col[i];other=col[other];
		int num=cnt(i,mid),dibu;
		if(!num) continue;
		for(reg int o=1;o<=num;o++) putt(other,n+1);
		for(reg int o=1;o<=m;o++){
			if(stack[i][o]<=mid){dibu=o;break;}
		}
		for(reg int o=m;o>=dibu;o--){
			if(stack[i][o]<=mid) putt(i,other);
			else putt(i,n+1);
		}
		for(reg int o=dibu;o<=m-num;o++) putt(n+1,i);
		for(reg int o=m-num+1;o<=m;o++) putt(other,i);
		for(reg int o=m-num+1;o<=m;o++) putt(n+1,other);
		i=backup;
	}//将所有 [l,mid] 颜色置于每个柱子的顶部
	int col1[52],col2[52];
	col1[0]=col2[0]=0;
	for(reg int i=1;i<=r-l;i++){
		int backup=i;
		int i1=col[i+1];i=col[i];
		int num1=cnt(i,mid),num2=cnt(i1,mid);
		if(num1+num2>=m){
			col1[++col1[0]]=i;
			for(reg int o=m;o;o--) putt(i,n+1);
			for(reg int o=m;stack[i1][o]<=mid&&o;o--) putt(i1,i);
			for(;top[i1]<m;) putt(n+1,i1);
			for(;top[i]<m;) putt(n+1,i);
		}
		else{
			col2[++col2[0]]=i;
			for(reg int o=m;stack[i][o]<=mid&&o;o--) putt(i,n+1);
			for(reg int o=m;stack[i1][o]<=mid&&o;o--) putt(i1,n+1);
			for(;top[i]<m;) putt(i1,i);
			for(;top[i1]<m;) putt(n+1,i1);
		}
		i=backup;
	}
	if(stack[col[r-l+1]][1]<=mid) col1[++col1[0]]=col[r-l+1];
	else col2[++col2[0]]=col[r-l+1];
	work(l,mid,col1);work(mid+1,r,col2);
}
int main(){
	n=read();m=read();
	for(reg int i=1;i<=n;i++){
		for(reg int j=1;j<=m;j++) stack[i][j]=read();
		top[i]=m;
	}
	int col[52];
	for(reg int i=1;i<=n;i++) col[i]=i;
	work(1,n,col);
	printf("%d\n",k);
	for(reg int i=1;i<=k;i++) printf("%d %d\n",A[i],B[i]);
	return 0;
}
posted @ 2020-12-08 23:37  suxxsfe  阅读(155)  评论(0编辑  收藏  举报