习题:小幸福(离散化)

                                                                                   小幸福
【题目描述】
有 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.
View Code

方法二

  读入同时进行标记,起始为1,终止为-1,快排后一边删重复点,一边将重复点标记累加到这个时间点上,然后也用计数器从头到尾地进行累加,然后统计,麻烦的有的点既有小朋友加入,又有小朋友离开不太好处理,加几个语句就行了,删重复点和累加都是O(n),但快排要O(nlogn),故时间效率是O(nlogn)。

代码

var
  a,b,c,f,g:array[0..200001]of longint;
  n,i,m,k,t,h,l,ans:longint;
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;  t:=b[i]; b[i]:=b[j]; b[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(a[i*2-1]); b[i*2-1]:=1; m:=m+1;
   end;
  for i:=1 to n do
   begin
    read(a[i*2]); b[i*2]:=-1; m:=m+1;
   end;
  qsort(1,m);
  if b[1]=1 then h:=1 else if b[1]=-1 then l:=1;
  for i:=2 to m+1 do
   begin
    if a[i]=a[i-1]  then
     begin
       if b[i]=1 then h:=h+1;
       if b[i]=-1 then l:=l+1;
     end
     else
      begin
        ans:=ans+1;
        c[ans]:=h; f[ans]:=h-l; g[ans]:=a[i-1]; h:=0;l:=0;
       if b[i]=1 then h:=h+1;
       if b[i]=-1 then l:=l+1;
     end;
    end;
 t:=0;
 m:=ans; ans:=0;
 for i:=1 to m do
  begin
   t:=t+f[i];
   if t>=k then ans:=ans+g[i+1]-g[i]; if (t<k)and(c[i]+t-f[i]>=k) then ans:=ans+1;
  end;
 writeln(ans);
 close(input); close(output);
end.
View Code

方法三(超时的离散化)

   一个超时的离散化,也贴一下做参考。

代码

program e;
var
  f:array[0..100000,1..2]of longint;
  a,b,s,t:array[0..100000]of longint;
  n,i,m,j,x,y,k,ans:longint;
function find(x:longint):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;
  exit(l);
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; 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
     x:=find(s[i]);
     y:=find(t[i]);
     for j:=x to y-1 do
      begin inc(f[j,1]); inc(f[j,2]); end;
     inc(f[y,1]);
   end;
  for i:=1 to m do
   begin  if f[i,2]>=k then ans:=ans+a[i+1]-a[i]; if (f[i,2]<k)and(f[i,1]>=k) then ans:=ans+1;  end;
  writeln(ans);
  close(input); close(output);
end.
View Code

方法四(线段树)

  线段树做的,也做参考。

代码

program e;
var
  a:array[0..10000000,1..3]of longint;
  s,t:array[0..100000]of longint;
  n,i,m,ans,k:longint;
procedure make(p,l,r:longint);
var mid:longint;
begin
  a[p,1]:=l; a[p,2]:=r; a[p,3]:=0;
  if l+1<r then
   begin
     mid:=(l+r) div 2;
     make(p*2,l,mid); make(p*2+1,mid+1,r);
   end;
end;
procedure put(p,l,r:longint);
var mid:longint;
begin
  if (a[p,1]=a[p,2]) then begin a[p,3]:=a[p,3]+1; exit; end;
  mid:=(a[p,1]+a[p,2]) div 2;
  if r<=mid then put(p*2,l,r)
   else if l>mid then put(p*2+1,l,r)
    else
      begin
        put(p*2,l,mid); put(p*2+1,mid+1,r);
      end;
  a[p,3]:=a[p*2,3]+a[p*2+1,3];
end;
procedure get(p:longint);
var mid:longint;
begin
  if a[p,1]=a[p,2] then inc(ans,ord(a[p,3]>=k))
   else begin get(p*2); get(p*2+1); end;
end;
begin
  assign(input,'e.in');
reset(input);
assign(output,'e.out');
rewrite(output);
  readln(n,k);
  for i:=1 to n do
   read(s[i]);
  for i:=1 to n do
  begin read(t[i]); if t[i]>m then m:=t[i]; end;
  make(1,1,m);
  for i:=1 to n do
   begin
     put(1,s[i],t[i]);
   end;
  get(1);
  writeln(ans);
  close(input); close(output);
end.
View Code

 

posted @ 2015-02-15 21:46  QTY_YTQ  阅读(369)  评论(0编辑  收藏