Description
有n个事件,每个事件用其发生的位置s1和时间t1来描述,而每一个事件都有其因事件,若一个事件位置s2和时间t2满足|t2-t1|>=|x2-x1|,则该事件成为原事件的因事件。现给出m个因事件,对于这m个”因事件”点集,可能是所有点的因事件,也可能是某个或某几个点的因事件。求全部因事件点中时间最早的那个点的时间最大值
Input
多组用例,第一行为用例组数T,对于每组用例,第一行包括事件数n和因事件数m,之后n行为n个时间的发生的时间和位置
Output
对于每组用例,输出其因事件中最早发生时间
Sample Input
4
4 1
1 -1
1 3
1 4
2 6
4 2
1 -1
1 3
1 4
2 6
4 3
1 -1
1 3
1 4
2 6
4 4
1 -1
1 3
1 4
2 6
Sample Output
Case 1: -2
Case 2: 0
Case 3: 0
Case 4: 1
Solution
记点(x,y)代表一个事件,x为事件发生位置,y为事件发生时间。在坐标系中以一个事件的坐标点做斜率分别为1和-1的两条直线,则在这两条直线下方的点即为该事件的因事件取值范围。故此题转化为在所有事件点中所对应的因事件范围内找m个点,使这m个点能够导致这n个事件发生,输出m个因事件中时间最大值。
那么二分目标时间,求出每个点的因事件在y=t这条直线上可能存在的坐标范围,遍历排序后的所有点,尽可能的计算出相邻两个点的可能范围的相交区间,若没有相交,则需要新增一个因事件点。这样算出来需要的总点数如果不超过m则记录答案,然后继续二分。
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100010
struct node
{
int x,y;
}P[maxn];
int T,n,m;
int cmp(node a,node b)
{
if(a.x==b.x)//位置相同则按时间升序排
return a.y<b.y;
return a.x<b.x;//否则按位置升序排
}
bool OK(int t)
{
int ll=t-P[0].y+P[0].x;//ll为第一个事件在y=t上因事件范围左端点
int rr=P[0].y-t+P[0].x;//rr为第一个事件在y=t上因事件范围右端点
int count=1;//初始为一个因事件
for(int i=1;i<n;i++)
{
int l=t-P[i].y+P[i].x;//l为第i个事件在y=t上因事件范围左端点
int r=P[i].y-t+P[i].x;//r为第i个事件在y=t上因事件范围右端点
if(rr<l)//上一个事件的因事件均不能作为该事件的因事件
{
count++;//因事件个数加一
rr=r;//更新因事件范围右端点
ll=l;//更新因事件范围左端点
}
else if(ll<=l&&rr>=l&&rr<=r)//两个时间的因事件范围有重叠
ll=l;//更新因事件范围左端点
else if(ll<=l&&rr>r)//上一个事件的因事件范围覆盖该事件的因事件范围
{
rr=r;//更新因事件范围右端点
ll=l;//更新因事件范围左端点
}
if(count>m)//因事件个数大于m,返回false
return false;
}
return true;//因事件个数小于m,返回true
}
int main()
{
scanf("%d",&T);
int res=0;
while(T--)
{
scanf("%d%d",&n,&m);
scanf("%d%d",&P[0].y,&P[0].x);//先输入时间,后输入位置
int l=-2000020,r=P[0].y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&P[i].y,&P[i].x);
r=min(r,P[i].y);//二分右极限为所有事件发生时间最小值
}
int ans;
sort(P,P+n,cmp);//对事件点排序
while(l<=r)//二分目标时间
{
int mid=(l+r)/2;
if(OK(mid))
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
printf("Case %d: %d\n",++res,ans);//按格式输出
}
return 0;
}