CF1733E Conveyor(2700*) 题解(神奇DP)

题意

题面

简要题意:
        有一个 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 1q104,0t1018,0x,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 txy 秒生成的。即它是第 t − x − y + 1 t - x - y + 1 txy+1 滴水滴(第 0 0 0 秒生成了第一滴)。
        如果我们能够求出第 t − x − y + 1 t - x - y + 1 txy+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=txy。然后转移就是 ⌊ 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,jdpi+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,jdpi,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值