小幸福
【题目描述】
有 n 个小朋友,他们商量在保证作业做完的前提下出去玩。第 i 个小朋友的可以玩耍时间为 Si~Ti。这里 Si~Ti 表示的是时间段,比如 Si=2,Ti=4,那么意味着这位小朋友在时刻 1 不能玩, 时刻 2、3、4 可以去玩,时刻 4 以后都不能出去玩。如果在某个时刻,在一起玩的小朋友个数 不少 K 个,那么这一时刻就是幸福的。现在你要求出所有幸福的时刻长度。
【输入描述】三行 第一行:n k (n 个小朋友,一起玩的小朋友达到 k 个为幸福) 第二行:S1 S2 ... Sn 第三行:T1 T2 ... Tn
【输出描述】一行:幸福时刻的长度
【输入样例】
4 3
1 2 2 4
5 2 4 6
【输出样例】
2
【样例说明】
时刻 1 2 3 4 5 6
第一个小朋友玩耍时间: X X X X X
第二个小朋友玩耍时间: X
第三个小朋友玩耍时间: X X X
第四个小朋友玩耍时间: X X X
第 2 分钟和第 4 分钟一起玩耍的小朋友达到了 3 个所以是幸福的时刻,幸福时刻长 度为 2。
【数据范围】
50% n<=1000 1<=Si<=Ti<=1000
100% n<=100000 1<=Si<=Ti<=1000000000
分析:
这道题有许多其他的版本,由于比较重要,所以发一下。
我们第一个会想到暴力算法,但很明显数据范围不允许,我们又会想到离散化和线段树,但一般离散化会超时,线段树会超空间,看了几个大牛的程序才得到思路。
方法一
先将点离散,然后运用二分查找确定每个小朋友玩耍起始终止时间在离散数组中的位置,分别用1和-1标记,然后用计数器从头加到尾,中途若计数器不小于k就统计该段长度,时间效率O(nlogn)。
代码
var s,t:array[0..100000]of longint; a,b,f:array[0..200000]of longint; n,i,m,k,u,ans:longint; procedure find(x,y:longint); var l,r,mid:longint; begin l:=1; r:=m; repeat mid:=(l+r) div 2; if x<=a[mid] then r:=mid else l:=mid+1; until l>=r; f[l]:=f[l]+y; end; procedure qsort(l,h:longint); var i,j,t,m:longint; begin i:=l; j:=h; m:=a[(i+j) div 2]; repeat while a[i]<m do inc(i); while m<a[j] do dec(j); if i<=j then begin t:=a[i]; a[i]:=a[j]; a[j]:=t; inc(i); dec(j); end; until i>j; if i<h then qsort(i,h); if j>l then qsort(l,j); end; begin assign(input,'e.in'); reset(input); assign(output,'e.out'); rewrite(output); readln(n,k); for i:=1 to n do begin read(s[i]); m:=m+1; a[m]:=s[i]; end; for i:=1 to n do begin read(t[i]); m:=m+1;t[i]:=t[i]+1; a[m]:=t[i]; end; qsort(1,m); b[1]:=a[1]; ans:=1; for i:=2 to m do if a[i]<>a[i-1] then begin ans:=ans+1; b[ans]:=a[i]; end; a:=b; m:=ans; ans:=0; for i:=1 to n do begin find(s[i],1); find(t[i],-1); end; for i:=1 to m-1 do begin u:=u+f[i]; if u>=k then ans:=ans+a[i+1]-a[i]; end; u:=u+f[m]; if u>=k then ans:=ans+1; writeln(ans); close(input); close(output); end.