CF1733E Conveyor
题意
有一个 \(120 \times 120\) 的棋盘,每个格子上有一条传送带,初始方向全部向右。
每一秒发生的事(按顺序)
- 移动:所有史莱姆按照自己所在格子的传送带方向移动一格。如果移出棋盘就消失;如果多个史莱姆移到同一格,合并成一个。
- 转向:所有上一秒有史莱姆的格子,传送带方向翻转(右变下,下变右)。
- 新生:\((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 逐格计算:
其中从左边来的贡献是 \(\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;
}

浙公网安备 33010602011771号