[NOIP2016 普及组] 海港
题目背景
NOIP2016 普及组 T3
题目描述
小 K 是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。
小 K 对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第 艘到达的船,他记录了这艘船到达的时间 (单位:秒),船上的乘客数 ,以及每名乘客的国籍 。
小K统计了 艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的 小时( 小时 秒)内所有乘船到达的乘客来自多少个不同的国家。
形式化地讲,你需要计算 条信息。对于输出的第 条信息,你需要统计满足 的船只 ,在所有的 中,总共有多少个不同的数。
输入格式
第一行输入一个正整数 ,表示小 K 统计了 艘船的信息。
接下来 行,每行描述一艘船的信息:前两个整数 和 分别表示这艘船到达海港的时间和船上的乘客数量,接下来 个整数 表示船上乘客的国籍。
保证输入的 是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 秒到达海港。
保证 ,$\sum{k_i} \le 3\times 10^5 $ ,, 。
其中 表示所有的 的和。
输出格式
输出 行,第 行输出一个整数表示第 艘船到达后的统计信息。
样例 #1
样例输入 #1
3
1 4 4 1 2 2
2 2 2 3
10 1 3
样例输出 #1
3
4
4
样例 #2
样例输入 #2
4
1 4 1 2 2 3
3 2 2 3
86401 2 3 4
86402 1 5
样例输出 #2
3
3
3
4
提示
【样例解释 1】
第一艘船在第 秒到达海港,最近 小时到达的船是第一艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家;
第二艘船在第 秒到达海港,最近 小时到达的船是第一艘船和第二艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家;
第三艘船在第 秒到达海港,最近 小时到达的船是第一艘船、第二艘船和第三艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家。
【样例解释 2】
第一艘船在第 秒到达海港,最近 小时到达的船是第一艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家。
第二艘船在第 秒到达海港,最近 小时到达的船是第一艘船和第二艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家。
第三艘船在第 秒到达海港,最近 小时到达的船是第二艘船和第三艘船,共有 个乘客,分别是来自国家 ,共来自 个不同的国家。
第四艘船在第 秒到达海港,最近 小时到达的船是第二艘船、第三艘船和第四艘船,共有 个乘客,分别是来自国家 ,共来自 个 不同的国家。
【数据范围】
- 对于 的测试点,。
- 对于 的测试点,。
- 对于 的测试点,。
- 对于 的测试点,。
- 对于 的测试点,。
题目分析
阅读题面,可发现题目要求的是:每艘船到达后,24小时内的不同国家的数量。也就是连续区间内不同元素的个数。那么可以采用尺取法的思想来完成这道题目。该区间的特征即为24小时内,那么只需左、右端点的时间差不超过86400即可。 。
区间的特征判断已处理好,接下来要解决的是:如何统计连续区间内的不同元素个数?
对于每艘船输入的信息有:
- 到达时间(输入的是递增的)
- 每艘船的人数 k
- 每个人的国籍信息
可发现,每艘船的到达时间是递增的,也就是呈单调性。每艘船上的人的到达时间是相同的。可将当前船只到达的时间视为区间结束的时间,先根据区间结束时间,将区间开头的元素进行删除,删除掉与区间结束时间差超过24小时的元素。针对每个删除的元素,减去对应国籍统计出的人数,当人数减少至0时,不同国籍数量减一。对应的,将该艘船到达的所有人加入区间,每个人对应的国籍人数增加,若该国籍人数之前为0,则不同国籍数量加一。
人员总数 ,所以可以定义一个计数数组cnt[]
,cnt[x]
为国籍x在24小时内对应的人数。这样能方便统计国籍对应的人数数量,整体时间复杂度为。
可用双指针来描述连续区间的左、右端点。
//将24小时外的出队
while(L<=R && cha[L].ti+86400<=t){
cnt[cha[L].x]--;//离开区间的元素对应国籍人数减少
if(cnt[cha[L].x]==0) ans--;//若减少至0,不同国籍数减一
L++;//移动左指针
}
for(int j=1;j<=k;j++){//输入该艘船k个人的信息
scanf("%d",&x);
if(cnt[x]==0) ans++;//统计新出现的国家
cnt[x]++;//国籍x对应人数增加
cha[++R]=node{t,x};//存入数组中,并移动右指针
}
代码实现(双指针)
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int N=3e5+5;
int n,t,k,x;
int cnt[N];//cnt[i],国家i的人数
struct node{
int ti,x;//到达时间、国籍
};
node cha[N];
int main(){
int ans=0;//24小时内,不同国家的数量
scanf("%d",&n);
int L=1,R=0;
for(int i=1;i<=n;++i){
scanf("%d%d",&t,&k);
//将24小时外的出队
while(L<=R && cha[L].ti+86400<=t){
cnt[cha[L].x]--;//离开区间的元素对应国籍人数减少
if(cnt[cha[L].x]==0) ans--;//若减少至0,不同国籍数减一
L++;//移动左指针
}
for(int j=1;j<=k;j++){//输入该艘船k个人的信息
scanf("%d",&x);
if(cnt[x]==0) ans++;//统计新出现的国家
cnt[x]++;//国籍x对应人数增加
cha[++R]=node{t,x};//存入数组中,并移动右指针
}
printf("%d\n",ans);
}
return 0;
}
代码实现(队列)
除了双指针,也可以采用队列来进行实现,队列的队首即区间的左端点;队尾即区间的右端点。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int N=3e5+5;
int n,t,k,x;
int cnt[N];//cnt[i],国家i的人数
struct node{
int ti,x;//到达时间、国籍
};
queue<node> q;
int main(){
int ans=0;//24小时内,不同国家的数量
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&t,&k);
//将24小时外的出队
while(q.size() && q.front().ti+86400<=t){
int x=q.front().x;
q.pop();
cnt[x]--;//x国家人数减少
if(cnt[x]==0) ans--;//若减少至0则不同国籍数量减一
}
for(int j=1;j<=k;++j){//输入该艘船k个人的信息
scanf("%d",&x);
if(cnt[x]==0) ans++;//增加新的国籍
cnt[x]++;//统计国籍数
q.push(node{t,x});//入队
}
printf("%d\n",ans);
}
return 0;
}