Channel On Live (EOJ Monthly 2018.8)掃描線 代替 樹狀數組 簡化求解

 
 
   

Time limit per test: 3.0 seconds

Memory limit: 512 megabytes

 
   

EOJ has recently opened a new live channel, so that coders can broadcast their coding online and chat with the audience at the same time. It has become children’s favorite entertainment at night, well, other than the monthly contest.

It may sound hard to believe, but the EOJ team has recently encountered some trouble in terms of the live channel statistics. Basically they want to know two things:

  • The maximum number of children watching the live channel simultaneously;
  • The average number of children online, during the show, which starts at the beginning of second 1 and ends atthe end of second m.

Input

The first line contains two space-separated integers n and m (1n250 0001m109), denoting the number of children log-in record, the length of the show.

The next n line each contains two space-separated integers si and ti (1sitim), meaning that a child starts to watch the channel at the start of second si, and leave the channel at the end of second ti. Notice that the same pair of(s,t) might appear multiple times, they are believed to be different records.

You can safely assume that these records all belong to different children.

Output

In the first line, output the maximum number. You can write it as an integer, or a floating number, as you like.

In the second line, output the average number as a floating number.

The absolute or relative error should be not greater than 1012.

 
   

Examples

 
   

input

2 10
1 5
5 10

output

2
1.1
 
   

input

4 10
1 3
4 4
8 8
5 7

output

1
8E-1
 
   

input

5 10
5 6
4 7
3 8
2 9
1 10

output

5.000000000000000
3


本題大意:
  有一個時長為m的電視節目,給出n位觀眾的觀看時間段 Si-Ti 要求最高峰時同時觀看本節目的人數,和平均觀看量(所有觀眾的觀看時長 除以 節目時長m)

  沒學掃描線的我一眼看去,本題的解決方案是維護樹狀數組,但仔細看發現m的範圍高達1e9,沒有辦法開數組,接著就會聯想到離散化。完成這些操作需要的時間和代碼量都不少。
其實本題用掃描線,就變得十分的簡單。(見以下代碼)

  反思一下我做這題時走彎路的原因:
  1、沒有學習掃描線算法(這不是重要原因)
  2、僵化了樹狀數組是思想,看到維護線段就想到樹狀數組

  實際上,不用看成線段反而更簡單,把每個觀眾“開始觀看”和“結束觀看”作為節點。遇到開始節點人數+1,遇到結束節點人數-1,這樣順序掃描所有節點,就反饋了人數的變化情況,
記錄下最高的峰值就是最大同事觀看人數。這裡還有一個注意點,由於離開的操作在+1個時間點才反應出來,比如:在第3秒鐘離開,應該在第4秒時-1,則在第四秒的時候如果有人進來,
就會出現重疊,而操作是有先後順序的,如果先加再減,就會出現人數虛高的情況,因而應該先減少再增加,防止虛高的人數(見代碼理解)


以下貼上代碼
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define N 500009
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
ll m,n;
struct Edge
{
    ll a,b;//a是時間點,b是1或-1,代表人數的增加或減少
}edge[N];
int cmp(Edge a,Edge b)
{
    if(a.a==b.a)
        return a.b<b.b;//同一個時間若有人離開,先減再加,故帶有-1的要排在帶有1的前面
    return a.a<b.a;
}
int main()
{
    ll a,b;
    ll sum=0;
    scanf("%lld%lld",&n,&m);
    for(ll i=0;i<2*n;i+=2)
    {
        scanf("%lld%lld",&a,&b);//開始時間,結束時間,作為兩個點進行創建
        edge[i].a=a;//開始時間
        edge[i].b=1;//操作是+1

        edge[i+1].a=b+1;//結束時間(+1) 因為下一秒他就不見,故他的消失反映在下一秒
        edge[i+1].b=-1;//操作是-1
        sum+=b-a+1;//記錄本次時長

    }

    sort(edge,edge+2*n,cmp);//把所有節點排序,從而形成時間
    int cnt=0,ans=0;
    for(ll i=0;i<2*n;i++)//按時間順序遍歷
    {
        cnt+=edge[i].b;//這裡反映著+1和-1的變化,就是人數的波動變化
        if(ans<cnt)
            ans=cnt;
    }
    printf("%d\n",ans);
    printf("%.14lf",sum*1.0/m);
    return 0;
}
 
   
 
經過本題,積累經驗:活動思維,不被某種形式對應的數據結構所困,不要被這裡維護區間的操作為束縛在樹狀數組或者線段樹上。看做時間點,掃描線操作簡單易行

猜你喜欢

转载自www.cnblogs.com/Lin88/p/9464468.html
今日推荐