codeforces-#657-D

codeforces-#657-D

七月 24, 2020

D. New Passenger Trams

题意:

某个星球的一天有h小时,一小时有m分钟,现在有两种车要开,客色的每半小时开一次,每次要等待k(k<=m/2)分钟再开。货车有固定的时刻表,且没有准备时间,但是它与客车的等待时间重合了就会被取消掉(端点重合不取消),问你第一辆客车车的出发时间t(t<=m/2)是多少才能使最少的货车被取消。输出最少要取消几辆以及第一辆客车的出发时间和被取消的货车是那几班

题解

事实上由于每个客车半小时发一趟车,客车每小时只会出发两趟 所以客车从到站到出发的时间段实际上是以半小时为周期的, 在一小时60分种的情况下若下一趟的车在20分到 后40出发 即相当于 0到30分中 [20,30]和[0,10]分中有客车在等待 货车不能发车,所以将所有的货车出发时间映射到同一个半小时时间段内 仅仅讨论这半个小时内 哪段长度为k(客车等待时间)的时间段内所覆盖到的货车发车班次最少即最优解将所有货车映射到同一个半小时内后 第l趟货车和第r趟货车若发车间隔>=k那么它们就不会被同一个客车等候时间被取消,我们要通过双指针得到一个区间左右端点上货车发车间隔<k且最少货车发车的时间段确定了这个时间段后,第一班客车发车的时间t=右端点r位置的货车发车时间 因为经验上,客车到站或出站与某一辆货车的发车时间重合时可以取到最优 (突变点)

这里train[n+1]=train[1]+m/2 目的是为了更好的处理客车等候时长由于取模(环变链)导致的连续区间分裂 这样双指针就不会因为时长为k的区间一半在前一半在后而变得复杂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define Turnoff std::ios::sync_with_stdio(false)
const ll Max=2e5+5;
struct Train{
ll mi;
int indx;
} train[Max];
ll n,h,m,k,half;
bool cmp(Train x,Train y){return x.mi<y.mi;}
int main() {
Turnoff;
cin>>n>>h>>m>>k;half=m/2;
for(int i=0;i<n;i++){
///把所有货车出发的时间都对half取模映射到半小时内的时间段讨论
///因为所有客车都是以半小时为周期发车的
///所以讨论半小时内就行
int hi,mi;cin>>hi>>mi;
train[i].mi=mi%half;
train[i].indx=i+1;
}
sort(train,train+n,cmp);
for(int i=0;i<n;i++){
///再多加n个货车每个货车的出发时间为mi+half方便处理环
///主要是考虑到有的客车等待的时间会跨越0~half 而被取模
///这样既不影响单调性也能满足取模的性质
train[i+n].mi=train[i].mi+half;
train[i+n].indx=train[i].indx;
}
ll bios;
int num=2e5;
for(int l=0,r=n;r<2*n;r++){
while(train[r].mi-train[l].mi>=k)l++;
///因为货车可以和客车同时出发或者在客车刚到时出发
///所以两趟货车的间隔>=k时不会同时被包括在一个客车等候时间段内
if(num>r-l){
num=r-l;
bios=train[r].mi;
///贪心策略
///加入偏移量t(bios)使得某个货车出发取模后的时间与第一辆客车出发时间
///(也可能是到站时间)重合时一定能求得一个最优解
}
}
cout<<num<<" "<<bios%half<<endl;
for(int i=0;i<2*n;i++){
///必须遍历2n个因为得到的bios是用多加入n个货车产生的,即bios的值在[0~m-1]
if(train[i].mi<bios&&train[i].mi>bios-k)cout<<train[i].indx<<" ";
}
cout<<endl;
}