HDU6119 小小粉丝度度熊

来源:互联网 发布:海口seo外包 编辑:程序博客网 时间:2024/04/27 23:12

思路:如果我们把覆盖的区间和未覆盖的分开,那么我们可以构造出一个序列:例如
7 5 4 3 5 9 8
粗体的表示中间没有签到的部分,而相邻的部分就是已经连续签到了的部分。

那么题目实际上就是我们可以单独的覆盖m天,然后怎么样覆盖才能获得最大的连续区间,然后求出这个连续的区间大小。
嗯,根据范围基本上确定是一个nlogn的做法。最容易想到的是暴力枚举区间,但显然n2,于是考虑能不能二分,嗯,然后看一下答案的性质,答案肯定是一个连续的区间,那这就好办了,那么我们可以枚举左端点,二分消耗不超过m的右端点的值。然后不断更新答案就可以了。复杂度O(nlogn)

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int MAXN = 1e5+7;const int inf = 1e9;const LL mod = 1e9+7;int n;int m;int a[MAXN];int b[MAXN];struct node{    int l,r;    bool operator < (const node &a)const    {        if(l!=a.l)return l < a.l;        else return r < a.r;    }} p[MAXN];int main(){    while(~scanf("%d%d",&n,&m))    {        for(int i = 0; i < n; ++i)scanf("%d%d",&p[i].l,&p[i].r);        sort(p,p+n);        int cnt = 0;        //给出的数据按照块和空白分开,做成一个序列        int ll = p[0].l,rr = p[0].r;        for(int i = 1; i < n; ++i)        {            if(p[i].l <= rr + 1)            {                rr = max(rr,p[i].r);                continue;            }            else            {                a[cnt++] = rr - ll +1;                b[cnt] = p[i].l - rr - 1;                ll = p[i].l;                rr = p[i].r;            }        }        a[cnt] = rr - ll + 1;        int MAX = 0;        //累加前缀和        for(int i = 1; i <= cnt; ++i)b[i]+=b[i-1],a[i]+=a[i-1];        //枚举左端点,二分右端点        for(int i = 0 ; i <= cnt; ++i)        {            //二分不超过m的最右端点            int low = i+1,high = cnt;            int ans = i;            while(low <= high)            {                int mid = (low + high)>>1;                if(b[mid] - b[i] <= m)                {                    ans = mid;                    low = mid+1;                }                else high = mid - 1;            }            MAX = max(MAX,(a[ans] - (i>0?a[i-1]:0)+m));        }        printf("%d\n",MAX);    }    return 0;}/*5 23 45 61 48 100200 300*/
原创粉丝点击