题意
简要题意:
有一个
120
120
120 行,
120
120
120 列的网格,行列均从
0
0
0 开始编号。
我们使用
(
x
,
y
)
(x, y)
(x,y) 表示从上到下第
x
x
x 行, 从左到右第
y
y
y 列 对应的方格。
这个网格上的每一个格子都有一个传送带,初始时所有传送带的方向都为右边。
初始时(第
0
0
0 秒),格子
(
0
,
0
)
(0, 0)
(0,0) 有一个水滴,每一秒这个传送带会这样变化:
1.所有水滴沿着所在传送带所指的方向,移动一个;
2.若水滴来到网格外,立即消失;若两个水滴同时来到一个方格,它们会合并;
3.所有在移动之前,上面有水的传送带,改变自己的方向,原来指向右边的现在指向下边,原来指向下边的现在指向右边;
4. 格子
(
0
,
0
)
(0, 0)
(0,0) 新出现一滴水。
给你
q
q
q 次询问,问在第
t
t
t 秒,
(
x
,
y
)
(x, y)
(x,y) 格子有没有水滴。
1
≤
q
≤
1
0
4
,
0
≤
t
≤
1
0
18
,
0
≤
x
,
y
<
120
1 \leq q \leq 10^4, 0 \leq t \leq 10^{18}, 0 \leq x, y < 120
1≤q≤104,0≤t≤1018,0≤x,y<120。
分析
我们发现
t
t
t 很大,但是由于
x
,
y
x, y
x,y 比较大,矩乘是没办法做的。我们考虑挖掘一些性质。
首先有一个性质,那就是水滴不可能合并。因为每次一个位置上的水滴要么向下,要么向右。我们定义 对角线
i
i
i 为所有满足 行数加列数等于
i
i
i 的位置,那么每一秒一滴水肯定是从对角线
x
x
x 移到了对角线
x
+
1
x + 1
x+1。由于每一滴水出现时间不同,所以水滴不可能合并。
这也同时说明了,如果询问
x
,
y
x, y
x,y 位置在
t
t
t 秒是否有水滴,那么如果有,这滴水一定是在
t
−
x
−
y
t - x - y
t−x−y 秒生成的。即它是第
t
−
x
−
y
+
1
t - x - y + 1
t−x−y+1 滴水滴(第
0
0
0 秒生成了第一滴)。
如果我们能够求出第
t
−
x
−
y
+
1
t - x - y + 1
t−x−y+1 滴水生成时,整个地图的方向,那么就可以
O
(
n
)
O(n)
O(n) 的模拟它的轨迹来判断了。
考虑如果 格子
(
x
,
y
)
(x, y)
(x,y) 经过了
k
k
k 滴水,会有
⌈
k
2
⌉
\left \lceil \frac{k}{2} \right \rceil
⌈2k⌉ 滴水跑到
(
x
,
y
+
1
)
(x, y + 1)
(x,y+1) 上,会有
⌊
k
2
⌋
\left \lfloor \frac{k}{2} \right \rfloor
⌊2k⌋ 滴水跑到
(
x
+
1
,
y
)
(x + 1, y)
(x+1,y) 上。所以这样我们就可以dp了。
对于每次询问,初始时令
d
p
0
,
0
=
t
−
x
−
y
dp_{0, 0} = t - x - y
dp0,0=t−x−y。然后转移就是
⌊
d
p
i
,
j
2
⌋
→
d
p
i
+
1
,
j
\left\lfloor \frac{dp_{i, j}}{2}\right\rfloor \to dp_{i + 1, j}
⌊2dpi,j⌋→dpi+1,j,
⌈
d
p
i
,
j
2
⌉
→
d
p
i
,
j
+
1
\left \lceil \frac{dp_{i, j}}{2} \right \rceil \to dp_{i, j + 1}
⌈2dpi,j⌉→dpi,j+1。
dp完后模拟。
CODE:
#include<bits/stdc++.h>// 第一条性质是水滴不会重合
using namespace std;// 第二条性质是假设 t 秒 (x, y) 有水滴,那么一定是第 t - x - y + 1 滴
const int N = 125;
typedef long long LL;
int q, x, y, n;
LL t, dp[N][N];
void solve(){
scanf("%lld%d%d", &t, &x, &y);
if(t < x + y) printf("NO\n");
else{
memset(dp, 0, sizeof dp);
dp[0][0] = (t - 1LL * x - y);
for(int i = 0; i <= n; i++){
for(int j = 0; j <= n; j++){
dp[i][j + 1] += (dp[i][j] + 1LL) / 2LL;
dp[i + 1][j] += dp[i][j] / 2LL;
}
}
int nx = 0, ny = 0;
for(int i = 1; i <= x + y; i++){
if(dp[nx][ny] & 1LL) nx++;
else ny++;
}
if(nx == x && ny == y) printf("YES\n");
else printf("NO\n");
}
}
int main(){
n = 120;
scanf("%d", &q);
while(q--) solve();
return 0;
}