CF1494C 1D Sokoban 贪心+二分

C. 1D Sokoban


time limit per test: 2 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output

You are playing a game similar to Sokoban on an infinite number line. The game is discrete, so you only consider integer positions on the line.

You start on a position \(0\). There are \(n\) boxes, the \(i\)-th box is on a position \(a_i\). All positions of the boxes are distinct. There are also \(m\) special positions, the \(j\)-th position is \(b_j\). All the special positions are also distinct.

In one move you can go one position to the left or to the right. If there is a box in the direction of your move, then you push the box to the next position in that direction. If the next position is taken by another box, then that box is also pushed to the next position, and so on. You can't go through the boxes. You can't pull the boxes towards you.

You are allowed to perform any number of moves (possibly, zero). Your goal is to place as many boxes on special positions as possible. Note that some boxes can be initially placed on special positions.


Input

The first line contains a single integer \(t(1\leqslant t \leqslant 1000)\) — the number of testcases.

Then descriptions of \(t\) testcases follow.

The first line of each testcase contains two integers \(n\) and \(m (1\leqslant n,m≤2⋅10^5)\) — the number of boxes and the number of special positions, respectively.

The second line of each testcase contains \(n\) distinct integers in the increasing order \(a_1,a_2,…,a_n (−10^9 \leqslant a_1<a_2<⋯<a_n \leqslant 10^9; ai≠0)\) — the initial positions of the boxes.

The third line of each testcase contains \(m\) distinct integers in the increasing order \(b_1,b_2,…,b_m (−10^9 \leqslant b_1<b_2<⋯<b_m \leqslant 10^9; bi≠0)\) — the special positions.

The sum of \(n\) over all testcases doesn't exceed \(2⋅10^5\). The sum of m over all testcases doesn't exceed \(2⋅10^5\).


Output

For each testcase print a single integer — the maximum number of boxes that can be placed on special positions.


Example

input

5
5 6
-1 1 5 11 15
-4 -3 -2 6 7 15
2 2
-1 1
-1000000000 1000000000
2 2
-1000000000 1000000000
-1 1
3 5
-1 1 2
-2 -1 1 2 5
2 1
1 2
10

output

4
2
0
3
1

Note

In the first testcase you can go \(5\) to the right: the box on position \(1\) gets pushed to position \(6\) and the box on position \(5\) gets pushed to position \(7\). Then you can go \(6\) to the left to end up on position \(−1\) and push a box to \(−2\). At the end, the boxes are on positions \([−2,6,7,11,15]\), respectively. Among them positions \([−2,6,7,15]\) are special, thus, the answer is \(4\).

In the second testcase you can push the box from \(−1\) to \(−10^9\), then the box from \(1\) to \(10^9\) and obtain the answer \(2\).

The third testcase showcases that you are not allowed to pull the boxes, thus, you can't bring them closer to special positions.

In the fourth testcase all the boxes are already on special positions, so you can do nothing and still obtain the answer \(3\).

In the fifth testcase there are fewer special positions than boxes. You can move either \(8\) or \(9\) to the right to have some box on position \(10\).

C.1维 仓库番


时间限制:2s
空间限制:256MB
输入格式:标准输入格式
输出格式:标准输出格式

关于仓库番:
摘自百度百科:玩家必须依各关仓库内的地形,将各箱子推到指定的位置上。

你正在一个无限长的数轴上玩一个类似于仓库番的游戏。游戏是离散形式的,所以你只需要考虑数轴上的整数位置。

你在位置\(0\)开始游戏。有\(n\)个箱子,第\(i\)个箱子的位置为\(a_i\)。所有箱子的位置都是独一无二的。有\(m\)个特殊点,第\(j\)个特殊点的位置为\(b_j\)。所有特殊点的位置也都是独一无二的。

在一次移动中,你可选择往左或往右移动一个位置。如果你将要移动到的位置上有箱子,你就会把它推向你移动方向的下一个位置。如果下一个位置有另外一个箱子,那个箱子也会被推向下下个位置,以此类推。你不能穿过这些箱子。你不能把箱子拉过来。

你可以移动任意次(\(0\)次也是被允许的)。你的目标是让尽可能多的箱子处于特殊点。注意,有些箱子在一开始可能就会处于特殊点。


输入描述

第一行包含一个整数\(t(1\leqslant t \leqslant 1000)\)——代表测试用例的数量。

接下来是\(t\)个测试用例的描述。

对于每个测试用例,第一行为两个整数 \(n\)\(m (1\leqslant n,m≤2⋅10^5)\) ——分别为箱子的数量和特殊点的数量。

对于每个测试用例,第二行为\(n\)个升序的、各不相同的整数\(a_1,a_2,…,a_n (−10^9 \leqslant a_1<a_2<⋯<a_n \leqslant 10^9; ai≠0)\)——箱子的初始位置。

对于每个测试用例,第三行为\(m\)个升序的、各不相同的整数\(b_1,b_2,…,b_m (−10^9 \leqslant b_1<b_2<⋯<b_m \leqslant 10^9; bi≠0)\)——特殊点的位置。

所有测试用例的\(n\)之和不超过\(2⋅10^5\)。所有测试用例的\(m\)之和不超过\(2⋅10^5\)


输出描述

对于每组测试用例,输入一个整数——箱子能处于特殊点的最大值。


示例

输入样例

5
5 6
-1 1 5 11 15
-4 -3 -2 6 7 15
2 2
-1 1
-1000000000 1000000000
2 2
-1000000000 1000000000
-1 1
3 5
-1 1 2
-2 -1 1 2 5
2 1
1 2
10

输出样例

4
2
0
3
1

提示

在第一组测试用例中,你可以向右移动\(5\)格:在位置\(1\)上的箱子会被推到位置\(6\),然后在位置\(5\)上的箱子会被推到位置\(7\)。之后,你可以向左移动\(6\)格,最终停到位置\(-1\),并将此位置上的箱子推到位置\(-2\)。最终,所有箱子的位置分别为\([−2,6,7,11,15]\)。在这些箱子之中,位置\([−2,6,7,15]\)为特殊点,因此,答案为\(4\)

在第二组测试用例中,你可以将处于位置\(-1\)的箱子推到位置\(-10^9\),然后将处于位置\(1\)的箱子推到位置\(10^9\),最终获得答案为\(2\)

在第三组测试用例中展示了一种情况,你由于无法将箱子拉过来,所以你无法让它们靠近特殊点。

在第四组测试用例中,所有箱子都已经在特殊点上了,所以你可以什么都不做,依旧可以得到答案为\(3\)

在第五组测试用例中,特殊点的数量少于箱子的数量。你可以向右移动\(8\)\(9\)格来让某个箱子到达位置\(10\)



Solution

代码来自yzhx233666;

1.由于初始位置为\(0\),可以考虑往左和往右推箱子,让箱子连在一起,并使这些被推的箱子尽可能多得停在特殊电上;

2.具体实现上,由于箱子和特殊点位置太过离散,可以使用下标来代替它们在数轴上的位置;

3.通过遍历,假设将离初始位置\(0\)最近的箱子推到\(b_i\)位置上,会有多少箱子落到特殊点上,加上原本的留在特殊点上的箱子数量,即为一个答案;

4.注意要分向左推和向右推的区别,分情况讨论,具体细节见代码;

  • TAG:贪心;二分



std.cpp

/*
#: 109046487 	 	 	 	 	 	 	
When: Mar/04/2021 10:50UTC+9 	 	 	 	 	 	
Who: PotremZ
Problem: C - 1D Sokoban
Lang: GNU C++11
Verdict: Accepted
Time: 108 ms
Memory: 2400 KB
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+5;
int a[N],b[N],vis[N];
int main(){
	int T; scanf("%d",&T); while(T--){
		int n,m; scanf("%d %d",&n,&m);
		int sta=1,stb=1;
		for(int i=1;i<=n;++i){ vis[i]=0; scanf("%d",&a[i]); if(a[i-1]<0 && a[i]>0) sta=i; }
		//sta代表最初能被推动的箱子的下标
		for(int i=1;i<=m;++i){ vis[i]=0; scanf("%d",&b[i]); if(b[i-1]<0 && b[i]>0) stb=i; }
		//stb代表最初能到达的特殊点的下标
		for(int i=1;i<=n;++i){
			int x=lower_bound(b+1,b+m+1,a[i])-b;
			//在特殊点b[i]中寻找第一个大于等于箱子位置a[i]的位置的下标
			if(b[x]==a[i]) vis[x]=1;
			//如果两者相等,说明箱子已经在特殊点上,vis[x]代表当前特殊点已有箱子
		}
		int ans=0;
		if(a[n]<0 && sta==1) sta=n+1; 
		if(b[m]<0 && stb==1) stb=m+1;
		if(sta>1 && stb>1){
			//sta>1&&stb>1有两种情况:
			//1).箱子和特殊点的初始位置有正有负,且最后一个箱子/特殊点位置>1
			//2).箱子和特殊点的初始位置均为负数
			//也就是说,进入这个if,代表开始处理数轴负方向的数据了
			//这里也解释了为什么sta和stb开始都是1,1是最小的下标,而如果距离位置0最近的sta和stb都不是1,说明了一定会有负数数据
			for(int i=stb;i>=1;--i) vis[i]+=vis[i+1];
			//除了离位置0最近的特殊点外,利用前缀和,累加出 从[stb,1](倒序)已经处于特殊点的箱子的数量
			int res=0;
			for(int i=stb-1;i>=1;--i){
				int x=lower_bound(a+1,a+n+1,b[i])-a;
				//在箱子位置a[]中寻找第一个大于等于b[i]的位置的下标
				int num=sta-x;
				//到x位置中间箱子的数量
				//为什么没有+1?
				//这是根据sta和stb情况得出的,其均有负数数据,而sta、stb代表了离位置0最近的下标并且为正数数据,故需要去掉sta这个点
				int y=lower_bound(b+1,b+m+1,b[i]+num-1)-b;
				//在特殊点位置b[i]中寻找第一个大于等于b[i]+num-1的位置的下标
				//就是将离位置0最近的箱子移动到b[i]特殊点时,保证[b[i],b[i]+num-1]范围内都有箱子,寻找最后一个箱子离的最近的特殊点
				int nowans=y-i;
				if(b[y]==b[i]+num-1) nowans++;
				//nowans为当前[b[i],b[i]+num-1]范围内,特殊点的数量,其中b[y]点需要特判
				res=max(res,nowans+vis[1]-vis[i]);
				//vis[1]-vis[i]代表由于将箱子都移动到b[i]位置之前的位置,所以从b[i]之后的特殊点上原本有的箱子都被移动了,减去它们
			}
			ans+=res;
		}
		if(a[n]>0 && b[m]>0){
			//处理正数数据
			for(int i=stb;i<=m;++i) vis[i]+=vis[i-1];
			//前缀和累加,作用类比
			int res=0;
			for(int i=stb;i<=m;++i){
				int x=upper_bound(a+1,a+n+1,b[i])-a-1;
				//在箱子位置a[]中寻找第一个严格大于b[i]的位置的下标并-1,这样可以得到小于等于(离b[i])最近的箱子
				//与处理负数数据不同的是,负数数据的sta是不被关注的,它没有实际贡献,而正数数据的sta就代表了一个箱子,不能忽略它
				int num=x-sta+1;
				//+1正是因为不能忽略sta代表的箱子
				int y=lower_bound(b+1,b+m+1,b[i]-num+1)-b;
				int nowans=i-y+1;
				res=max(res,nowans+vis[m]-vis[i]);
			}
			ans+=res;
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-03-04 10:15  PotremZ  阅读(96)  评论(0编辑  收藏  举报