Live2D

CF1733E Conveyor

题意

有一个 \(120 \times 120\) 的棋盘,每个格子上有一条传送带,初始方向全部向右

每一秒发生的事(按顺序)

  1. 移动:所有史莱姆按照自己所在格子的传送带方向移动一格。如果移出棋盘就消失;如果多个史莱姆移到同一格,合并成一个。
  2. 转向:所有上一秒有史莱姆的格子,传送带方向翻转(右变下,下变右)。
  3. 新生\((0,0)\) 处出现一个新的史莱姆。

初始状态

  • \(t=0\) 时,只有 \((0,0)\) 有一个史莱姆
  • 所有传送带方向向右

询问

给定 \(q\) 个询问,每个询问给出 \(t, x, y\),问第 \(t\) 秒时,格子 \((x,y)\) 上是否有史莱姆。

输出

有输出 YES,没有输出 NO

思路

非常有趣的一道题目,但是我们显然可以马上发现一件事,合成史莱姆是不可能出现的,不信的可以自己去模拟多几组数据。
一眼看过去做法非常之多,看完数据范围直接就沉默了 \(10^{18}\) 明显最后的复杂度不可能和他有关。
史莱姆只有 2 种移动方法,向➡️或者向⬇️,显然比较好处理也有规律。
非常容易想到统计史莱姆的走向是不可能的,但是我们会发现格子数量的数据范围是比较小的,所以我们对每个格子进行计算。
假设有 \(x\) 只史莱姆经过这个点,那么非常显而易见的:
其中 \(\lceil x/2 \rceil\) 只会往右走,\(\lfloor x/2 \rfloor\) 只会往下走。
显然思路也就出来了:
\((0,0)\) 出发,如果前 \(k\) 秒(即前 \(k\) 只史莱姆)出发,那么在 \((0,0)\) 经过的总数是 \(k\)
然后按 DP 逐格计算:

\[\text{dp}[i][j] = \lceil \text{dp}[i][j-1] / 2 \rceil + \lfloor \text{dp}[i-1][j] / 2 \rfloor \]

其中从左边来的贡献是 \(\lceil \text{dp}[i][j-1]/2 \rceil\)(往右走的),从上面来的贡献是 \(\lfloor \text{dp}[i-1][j]/2 \rfloor\)(往下走的)。

判断方法也就呼之欲出了:

\(t\) 秒在 \((x,y)\) 的史莱姆,是第 \(t-(x+y)\) 秒从 \((0,0)\) 出发的那一只。所以只需要令 \(k = t - x - y + 1\),判断第 \(k\) 只史莱姆是否到达了 \((x,y)\)

判断方法:

  • 算出前 \(k\) 只史莱姆出发后,\((x,y)\) 累计经过的数量 \(A\)
  • 算出前 \(k-1\) 只史莱姆出发后,\((x,y)\) 累计经过的数量 \(B\)
  • 如果 \(A \ne B\),说明第 \(k\) 只史莱姆到达了 \((x,y)\),输出 YES
  • 否则输出 NO

代码

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

typedef long long ll;

int q;
ll t;
int x,y;

void calc(ll k,ll dp[120][120])
{
	memset(dp,0,sizeof(ll)*120*120);
	if(k<=0) return;
	dp[0][0]=k;
	for(int i=0;i<120;i++)
	{
		for(int j=0;j<120;j++)
		{
			if(i==0 && j==0) continue;
			ll a=0,b=0;
			if(j>0) a=(dp[i][j-1]+1)/2;
			if(i>0) b=dp[i-1][j]/2;
			dp[i][j]=a+b;
		}
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	cin>>q;
	while(q--)
	{
		cin>>t>>x>>y;

		if(t<(ll)x+y)
		{
			cout<<"NO\n";
			continue;
		}

		ll k=t-(ll)x-y+1;

		static ll d1[120][120],d2[120][120];

		calc(k,d1);
		calc(k-1,d2);

		if(d1[x][y]!=d2[x][y]) cout<<"YES\n";
		else cout<<"NO\n";
	}

	return 0;
}
posted @ 2026-09-20 22:24  小王小王i  阅读(2)  评论(0)    收藏  举报
Live2D