首页 > 其他分享 >P7115 [NOIP2020] 移球游戏

P7115 [NOIP2020] 移球游戏

时间:2024-11-20 17:18:52浏览次数:1  
标签:柱子 号球 P7115 int 复杂度 移球 NOIP2020 柱上 个球

link
这道题我觉得是我做到过极好的构造题了,思路和优化的方法都比较有特点,对数据点范围的分析已经对数据的给分也比较恰到好处。之前做了这道题,特此写一篇题解。

首先要批判一下给的小样例,对解题很容易起到反作用。所以构造题不能只看样例,要自己去手搓一下,这样才方便去做。

本题我们可以把数据点分为三个部分,其对应分别为N=2的部分,时间复杂度为 \(O(n^2 m)\) ,以及时间复杂度为 \(O(nmlogn)\)的部分。

Sub1:n=2

看到这道题,我们就应该想到一道题面看上去极其相似的题目:汉诺塔。于是我们可以仿照汉诺塔的思路,找规律来完成此题。

此时我们假设有三根柱子 \(x,y,z\) ,初始的球在 \(x,y\) 上,\(z\) 为空柱子。
此时我们想要把1号球全部移到一根柱子上。假设现在有 \(c\) 个球在 \(x\) 柱上。

Step1:将 \(y\) 柱上 \(c\) 个球移动到空的柱子 \(z\) 上。目的:为移动 \(x\) 柱上的球以分类提供空间。
Step2:对 \(x\) 柱上的每个球进行分讨。具体的说,若当前球为1号球,则将其移至 \(y\) 柱上;否则,将其移至 \(z\) 柱上。目的:区分出 \(x\) 柱上每一个球。
Step3:将 \(z\) 柱上的 \((m-c)\) 个球移动到 \(x\) 柱上。
Step4:将 \(y\) 柱上的 \(c\) 个球移动到 \(x\) 柱上。 Step3、Step4目的:将 \(x\) 柱上的球整理为1号球在上,2号球在下
Step5:将 \(z\) 柱上的 \(c\) 个球移动到 \(y\) 柱上。 目的:讲 \(z\) 柱腾空,方便下一步操作
Step6:将 \(x\) 柱上的 \(c\) 个球移动到 \(z\) 柱上。 目的:就是将1号球全部移到另一根柱子上
Step7:对 \(y\) 柱上的每一个球进行分讨。具体的说,若当前球为1号球,则将其移至 \(z\) 柱上;否则,将其移至 \(x\) 柱上。目的:完成操作

这样,七步操作结束后,我们可以得到 \(x\) 柱上全是2号球,\(z\) 柱上全是1号球。时间复杂度为 \(O(m)\),可以过掉n=2的测试点。同时,这些操作也是这道题目的根本。

Sub2

我们的目的是整理好n根柱子上的球。此时我们可以一根一根的去做,从n到2依次递归完成。

具体做法:

假设我们现在要整理好第 \(k\) 种球。此时第 \(i\) 号柱子上有 \(c_i\) 个颜色为 \(k\) 的球。

Step1:枚举 \(i\) :从1到n 目的:将所有颜色为 \(k\) 的球移到该柱子的顶端;

更具体一点的操作:
(一):将 \(k\) 柱上 \(c_i\) 个球移到 \((k+1)\) 柱上。 目的:为 \(k\) 柱移动出 \(c_i\) 个空位。
(二):对 \(i\) 柱上每一个球进行分讨。具体的说,若当前球颜色为 \(k\) ,则将其移动至 \(k\) 柱上,否则,则将其移动至 \((k+1)\) 柱上。目的:对 \(i\) 柱上每一个球进行区分筛选。
(三):将 \((k+1)\) 柱上 \((m-c_i)\) 个球移回 \(i\) 柱上。目的:没什么目的,就是把其他球移回原柱底部
(四):将 \(k\) 柱上 \(c_i\) 个球移回 \(i\) 柱上。 目的:没什么目的,就是把 \(k\) 球移动到原柱顶部
(五):将 \((k+1)\) 柱上 \(c_i\) 个球移至 \(k\)。 目的:为下次操作留好一个空柱子。

Step2:枚举 \(i\) :将 \(i\) 柱顶端为 \(k\) 的球移到 \((k+1)\) 柱上。

Step3:对 \(k\) 柱上每一个球进行分讨。具体的说,若当前球颜色为 \(k\) 则将其移动至 \((k+1)\) 柱上,否则,则将其随便移动到一个未满的柱子上。

然后我们递归去做每一次。一共要递归 \((n-1)\) 次。上述操作的时间复杂度为 \(O(nm)\) ,则总时间复杂度为 \(O(n^2m)\) ,可以得到70的高分。

Sub3

考虑对时间复杂度进行优化。上述过程可以分为两个部分去看,分别为执行操作与递归。发现操作的复杂度不好优化,选择优化递归。考虑多根颜色一起做,即分治算法。
建立一个 \(solve\) 函数,定义 \(mid=l+r>>1\),表示将所有颜色小于等于 \(mid\) 的球与颜色大于 \(mid\) 的球区分开。
即:不存在一根柱子上同时存在颜色大于等于 \(mid\) 的球与颜色小于 \(mid\) 的球,然后分治即可。

如何区分:每轮取出 \((r-l+1)\) 根柱子,每次取出两根柱子,若颜色小于等于 \(mid\) 的球数大于 \(m\) ,则任选 \(m\) 个小于等于 \(mid\) 的球,对其进行 \(Sub1\) 中的操作。大于同理。

分治总共要 \(logn\) 层。每次操作为 \(O(nm)\) 的时间复杂度,所以总时间复杂度为 \(O(nmlogn)\) 可以A掉此题。

这真是一道不可多得的构造好题娃。

哦还有代码

CODE

点击查看代码
#include<bits/stdc++.h>
//#define int long long 
#define pa pair<int,int>

#define x first

#define y second

#define pb push_back

#define mp make_pair


using namespace std;

const int N=60,M=410,Q=820001;

int n,m;
int em;
int top[N],a[N][M];
bool impx[M],impy[M];

int T;
pa ans[Q];

void move(int x,int y)
{
	ans[++T]=mp(x,y);
	a[y][++top[y]]=a[x][top[x]--];
}

int merge(int x,int y,int mid)
{
	int cx=0,cy=0;
	
	for(int i=1;i<=m;++i)
		impx[i]=(a[x][i]<=mid),impy[i]=(a[y][i]<=mid),
		cx+=impx[i],cy+=impy[i];
		
	if(cx+cy>m)
	{
		cx=m-cx,cy=m-cy;
		for(int i=1;i<=m;++i)
			impx[i]^=1,impy[i]^=1;
	}	
	
	for(int i=1;i<=m;++i)
		if(!impx[i] && cx+cy<m)
			impx[i]=1,++cx;	
				
	for(int i=cx;i;--i)	move(y,em);
	for(int i=m;i;--i)
		if(impx[i])	move(x,y);
		else move(x,em);
	for(int i=m-cx;i;--i)	move(em,x);
	for(int i=cx;i;--i)	move(y,x);
	for(int i=cx;i;--i)	move(em,y);
	for(int i=cx;i;--i)	move(x,em);
	for(int i=m;i;--i)
		if(impy[i])	move(y,em);
		else 		move(y,x);
		
	int p=em;
	em=y;
	return p;
}

void solve(int l,int r)
{
	if(l==r)	return;
	
	int mid=l+r>>1;
	vector<int> now;
	
	for(int i=1;i<=n+1;++i)
	{
		if(i==em)	continue;
		if(l<=a[i][1] && a[i][1]<=r)
			now.pb(i);
	}
	
	for(int i=0;i+1<now.size();++i)
		now[i+1]=merge(now[i],now[i+1],mid);
		
	solve(l,mid),solve(mid+1,r);
}

signed main()
{
	freopen("ball.in", "r", stdin);
    freopen("ball.out", "w", stdout);

//	ios::sync_with_stdio(false);
//	cin.tie(0);cout.tie(0);
	
	scanf("%d%d",&n,&m);
	em=n+1;
	
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%d",&a[i][j]);
			
	for(int i=1;i<=n;i++)	top[i]=m;
	
	solve(1,n);
	
	printf("%d\n",T);
	for(int i=1;i<=T;++i)
		printf("%d %d\n",ans[i].first,ans[i].second);
		
	return 0;
}

哦,留个问题:为什么这道题关闭了输入输出流的cin,cout会比scanf,printf慢那么多?差点洛谷上过不了~~~(我太鶸了)

标签:柱子,号球,P7115,int,复杂度,移球,NOIP2020,柱上,个球
From: https://www.cnblogs.com/tangml/p/18558851

相关文章

  • NOIP2020
    被树上的数打爆了,滚来写没有黑题的NOIP2020。排水系统题意:给定一张DAG,任意点度数不超过\(5\)。\(m\)个点有初始容量\(1\),一个点的容量会平均流给每条出边,求所有汇点的最终容量。数据范围:\(1\len\le10^5,\1\lem\le10\),保证任意一条源点到汇点的路径长不大于\(11\)......
  • 【NOIP2020普及组复赛】题1:优秀的拆分
    题1:优秀的拆分【题目描述】一般来说,一个正整数可以拆分成若干个正整数的和。例如,1=11=11=1,......
  • 【NOIP2020普及组复赛】题2:直播获奖
    题2:直播获奖【题目描述】NOI2130NOI2130NOI2130即将举行。为了增加观赏性,......
  • P7114 [NOIP2020] 字符串匹配
    P7114[NOIP2020]字符串匹配看到循环部分\(AB\),自然想要去枚举它,并且用哈希。开始想到的是倍增+hash求出最长循环的右端点,复杂度是\(O(n\logn)\),结果不好写,没写出来。我们先思考找到右端点怎么计算贡献。最朴素的,我们再枚举前缀\(ABAB\cdotsAB\),容易预处理出后缀出现奇数......
  • noip2020
    NOIp2020游记第一次打NOIp,有点小紧张/kel8:30开考,8:15进考场顺便带了一大包巧克力进场,想着考试的时候吃开考打开文件夹一看string就大窘了,字符串算法刚好没学啊/fadT1一看第一反应网络流,大喜,前两天刚复习兴致勃勃准备开始打dinic,然后发现这题和网络流一点关系没有随便打......
  • P7114 [NOIP2020] 字符串匹配
    Link:https://www.luogu.com.cn/problem/P7114知识点:枚举,结论,Z函数,哈希唉,三年了,三年!!!简述\(T\)组数据,每组数据给定仅由小写字母组成的字符串\(s\),求\(t={(AB)}^iC\)的方案数,其中\(F(A)\leF(C)\),其中\(F(t)\)表示字符串\(t\)中出现奇数次的字符的数量。两种方案不......
  • P7116 [NOIP2020] 微信步数
    原题简化题意:有一个k维场地,第i维宽为wi,即第i维的合法坐标为1,2,···,wi。小C有一个长为n的行动序列,第i元素为二元组(ci,di),表示这次行动小C的坐标由(x1,x2,...,xci,...,xk)变为(x1,x2,...,xci+di,...,xk)。小C会将行动......
  • NOIP2020游记
    (把很久之前博客漏掉的一篇搬上来了,以此勉励自己每次考试测一遍极限数据,观察大样例)看到这篇游记,您会发现2/3年前的zbs是多么naive啊!时间2020年12月5日下午以下正文:这是我到初二为止获得的第四个二等奖了,离一等,就差把乘/除号换个位置的距离啊!Day-1周五像往常一样回家,瞬间躺......
  • [NOIP2020] 移球游戏 解题报告
    题目描述给定\(n+1\)个栈,栈的高度限制为\(m\)。初始时前\(n\)个上每个有\(m\)个球,最后一个为空。球分为\(n\)种颜色,每种恰好\(m\)个。一次操作可以把一个栈顶的元素弹出放到一个另一个栈顶,但是不可以使栈溢出或下溢。现要把同种颜色的球移动到同一个栈上,你需要构造一......
  • NOIP2020 T2 字符串匹配【题解】
    NOIP2020T2字符串匹配首先声明这篇题解存在大多数让我这种人看懂的废话,如果想要速通,请另寻他解题目简化定义字符串乘法为\(AB\)为把两个字符串拼起来,定义阶乘\(A^i\)表示\(\prod_{1}^iA\)再定义\(F(S)\)为\(S\)中出现奇数次字符的数量现给定一个字符串\(S\),求......