P7115 [NOIP2020] 移球游戏 思路简记

好题

先手玩一下 n=2 (只有颜色 0/1) 的情况:

我们假设柱子 1 上有 s1,那就先把柱子 2 顶端的 s 个球移到 3,变成这样:

然后把柱子 1 的所有 1 移到柱子 2 上,所有 0 移到柱子 3上:

然后再把这些移走的 1/0 按顺序移回到柱子 1,并把柱子 2 剩下的 ms 个数移到柱子 3 上:

然后把柱子 1 上的 ms0 移到柱子 2 上:

最后再将柱子 3 所有的 1 移到柱子 1 上,所有的 0 移到柱子 2 上,那么就移好了:

总操作次数为 5ms

考虑能不能把多种颜色的情况转换成很多个两种颜色的问题

可以想到我们设一个值 x 使 x 的数全部变为 1>x 的数全部变为 0,然后将 [1,x][x+1,n] 分治求解。

很显然对于每个区间 [l,r] ,将 x 设为 l+r2 时操作次数是最优的。

每次 solve(l,r) ,我们将 [l,mid][mid+1,r] 中没有复原的柱子两两匹配,我们令两根柱子分别为 ij,这样,ij 中如果 mid 的个数大于 >mid 的个数,那么将 i 还原,否则将 j 还原,然后继续递归下去 solve(l,mid)solve(mid+1,r) 就行了。

计算一下总操作次数:

T(n)=2T(n/2)+5nm

那么

T(n)=5nmlogn

计算一下,这是 ok

#include<bits/stdc++.h>
using namespace std;

const int N=55;
const int M=405;

#define tp(x) a[x][top[x]]

int n,m,tot;
int a[N][M],top[N];
int ans[820005][2];

inline void move(int x,int y){
	ans[++tot][0]=x,ans[tot][1]=y;
	a[y][++top[y]]=a[x][top[x]--];
}

inline void solve(int l,int r){
	if(l==r) return;
	int mid=l+r>>1;
	bitset <N> vis;
	for(int i=l;i<=mid;++i)
		for(int j=mid+1;j<=r;++j){
			if(vis[i]||vis[j]) continue;
			int cnt=0;
			for(int k=1;k<=m;++k)
				cnt+=(a[i][k]<=mid)+(a[j][k]<=mid);
			if(cnt>=m){
				cnt=0;
				for(int k=1;k<=m;++k) cnt+=(a[i][k]<=mid);
				for(int k=1;k<=cnt;++k) move(j,n+1);
				while(top[i]) move(i,tp(i)<=mid?j:n+1);
				for(int k=1;k<=cnt;++k) move(j,i);
				for(int k=1;k<=m-cnt;++k) move(n+1,i);
				for(int k=1;k<=m-cnt;++k) move(j,n+1);
				for(int k=1;k<=m-cnt;++k) move(i,j);
				while(top[n+1]) move(n+1,(top[i]!=m&&tp(n+1)<=mid)?i:j);
				vis[i]=1;
			}
			else{
				cnt=0;
				for(int k=1;k<=m;++k) cnt+=(a[j][k]>mid);
				for(int k=1;k<=cnt;++k) move(i,n+1);
				while(top[j]) move(j,tp(j)>mid?i:n+1);
				for(int k=1;k<=cnt;++k) move(i,j);
				for(int k=1;k<=m-cnt;++k) move(n+1,j);
				for(int k=1;k<=m-cnt;++k) move(i,n+1);
				for(int k=1;k<=m-cnt;++k) move(j,i);
				while(top[n+1]) move(n+1,(top[j]!=m&&tp(n+1)>mid)?j:i);
				vis[j]=1;
			}
		}
	solve(l,mid),solve(mid+1,r);
}

signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
		cin>>a[i][++top[i]];
	solve(1,n);
	cout<<tot<<endl;
	for(int i=1;i<=tot;++i)
		cout<<ans[i][0]<<" "<<ans[i][1]<<endl;
}

作者:Into_qwq

出处:https://www.cnblogs.com/into-qwq/p/16629095.html

版权:本作品采用「qwq」许可协议进行许可。

posted @   Into_qwq  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示