hdu 6119 百度之星初赛B 小小粉丝度度熊

来源:互联网 发布:vb.net serialport1 编辑:程序博客网 时间:2024/05/15 23:50

题目:

小小粉丝度度熊

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1326    Accepted Submission(s): 428


Problem Description
度度熊喜欢着喵哈哈村的大明星——星星小姐。

为什么度度熊会喜欢星星小姐呢?

首先星星小姐笑起来非常动人,其次星星小姐唱歌也非常好听。

但这都不是最重要的,最重要的是,星星小姐拍的一手好代码!

于是度度熊关注了星星小姐的贴吧。

一开始度度熊决定每天都在星星小姐的贴吧里面签到。

但是度度熊是一个非常健忘的孩子,总有那么几天,度度熊忘记签到,于是就断掉了他的连续签到。

不过度度熊并不是非常悲伤,因为他有m张补签卡,每一张补签卡可以使得某一忘签到的天,变成签到的状态。

那么问题来了,在使用最多m张补签卡的情况下,度度熊最多连续签到多少天呢?

 

Input
本题包含若干组测试数据。

第一行两个整数n,m,表示有n个区间,这n个区间内的天数,度度熊都签到了;m表示m张补签卡。

接下来n行,每行两个整数(l[i],r[i]),表示度度熊从第l[i]天到第r[i]天,都进行了签到操作。


数据范围:

1<=n<=100000

0<=m<=1000000000
0<=l[i]<=r[i]<=1000000000


注意,区间可能存在交叉的情况。
 

Output
输出度度熊最多连续签到多少天。
 

Sample Input
2 11 13 31 21 1
 

Sample Output
33
Hint
样例一:度度熊补签第2天,然后第1天、第二天和第三天都进行了签到操作。样例二:度度熊补签第2天和第3天。



code1: 前缀和 + 二分 + 线段合并

#include <stdio.h>#include <iostream>#include <set>#include <algorithm>#include <iomanip>#include <string.h>#include <map>#include <set>#include <math.h>using namespace std;const int maxn=105000;typedef long long ll;int n,tot;///合并前后的线段数 ll m;struct node{    ll l, r;}rec[maxn],a[maxn];ll pre[maxn]; bool operator<(const node& a, const node& b){    return a.l<b.l;}///为哥思路: 前缀和 + 二分 + 线段合并  int main(){    while(~scanf("%d%lld", &n, &m)){        for(int i=1;i<=n;i++){            scanf("%lld%lld", &rec[i].l, &rec[i].r);        }        sort(rec+1, rec+1+n);///按照左端点排序         tot=0;        int last=-2;        for(int i=1;i<=n;i++){///相交的区间进行合并 后面处理起来方便很多             if(rec[i].l>last+1){                ++tot;                a[tot].l=rec[i].l;                a[tot].r=rec[i].r;            }            else {                a[tot].r=max(a[tot].r, rec[i].r);            }            last=a[tot].r;        }        pre[1]=0;        for(int i=2;i<=tot;i++){            pre[i]=pre[i-1]+a[i].l-a[i-1].r-1;///预处理出前 i 段线段中包含的没签到的天数         }        ll ans=0;        for(int i=1;i<=tot;i++){///枚举每一个线段的左端点,二分寻找最远的右端点使得两点之间所有天数都被覆盖             int l=i, r=tot;            int tag=-1;///记录最远线段编号             while(l<=r){                if(r-l<=1){                    if(pre[r]-pre[i]<=m) tag=r;                    else tag=l;                    break;                }                int mid=(l+r)/2;                if(pre[mid]-pre[i]<=m) l=mid;                else r=mid;            }            ll p=a[tag].r-a[i].l+1;            ll add=m-(pre[tag]-pre[i]);///a[i].l 到  a[tag].r中没用完的m 补在后面             ans=max(ans, p+add);        }        printf("%lld\n", ans);    }    return 0; }


code2:线段合并 + 尺取 

#include <stdio.h>#include <iostream>#include <set>#include <algorithm>#include <iomanip>#include <string.h>#include <map>#include <set>#include <math.h>using namespace std;const int maxn=105000;typedef long long ll;int n,tot;///合并前后的线段数 ll m,pre[maxn];struct node{    ll l, r;}rec[maxn],a[maxn];bool operator<(const node& a, const node& b){    return a.l<b.l;}///另一种思路:线段合并 + 尺取 int main(){///202MS2864K    while(scanf("%d%lld", &n, &m)==2){        for(int i=1;i<=n;i++){            scanf("%lld%lld", &rec[i].l, &rec[i].r);        }        sort(rec+1, rec+1+n);///按照左端点排序         tot=0;        int last=-2;        for(int i=1;i<=n;++i){///相交的区间进行合并 后面处理起来方便很多             if(rec[i].l>last+1){                ++tot;                a[tot].l=rec[i].l;                a[tot].r=rec[i].r;            }            else {                a[tot].r=max(a[tot].r, rec[i].r);            }            last=a[tot].r;        }        pre[0]=0;        for(int i=1;i<tot;++i){            pre[i]=a[i+1].l-a[i].r-1;/// pre[i]表示 i 和 i+1 两段区间之间空余的天数         }        ll ans=0;        int usedm=0,s=1,t=1;        for(;t<tot;++t){///枚举每一段空余天数 作为尺取右端点         usedm+=pre[t];        while(usedm>m){        usedm-=pre[s];        s++;        }        ans=max(ans,a[t+1].r-a[s].l+1 + (m-usedm));        }        for(int i=1;i<=tot;++i) ///考虑任意两段都连不起来的情况 ans=max(ans,a[1].r-a[1].l+1 + m);        printf("%lld\n", ans);    }    return 0; }



阅读全文
1 0
原创粉丝点击