2017"百度之星"程序设计大赛 - 初赛(B)1006 小小粉丝度度熊

这题的区间因为有重叠,先考虑把重叠无用的区间去掉,考虑怎么去掉。

如图,红色那种区间是完全没有用的,按区间左端点从小到大排序之后,如果一个区间的右端点,比上一个有效区间的右端点还要小的话,那么就是无用的了,应该要删掉这个区间。这个操作用set来实现很方便。

然后考虑签到次数,这个条件怎么用,考虑到区间的价值都是正的,满足递增性,那么可以用尺取的方法来实现。如果签到次数有剩余,那么就尽量往右包括多点区间,直到签到次数不够用了,左边就收缩,舍弃区间,归还签到次数。这样重复是可以找到一段连续的最大值的。




注意一下区间的计算方法,这个多造几组数据可以看出端倪

#include
#define max(a,b) a>b?a:b
using namespace std;
const int MAXN = 100000+10;
set > rset;//用pair来存区间
int n,m;
int main()
{
    if (fopen("in.txt", "r") != NULL)
    {
        freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
    }
    while(cin>>n>>m)
    {
        rset.clear();
        for(int i=0;i >::iterator itp = rset.begin(),it=rset.begin();
        it++;
        while(it!=rset.end())
        {
            if(it->secondsecond)
            {
                set >::iterator tmp = it;
                it++;//指针先右移,不然删掉就不能右移了
                rset.erase(tmp);//把这个无效区间删掉
            }
            else
            {
                it++;
                itp++;
            }
        }
        itp=rset.begin(),it=itp;
        int ans=0;
        int sum=itp->second - itp->first +1;
        ans=sum+m;
        int cost=0;
        while(1)//尺取
        {
            while(cost>m)//左缩
            {
                set >::iterator tmp = itp;
                itp++;
                cost-=max(0,itp->first-tmp->second-1);
                sum-=itp->first-tmp->first;
            }
            ans=max(ans,sum+m-cost);//更新最大值的时候把签到次数给用完
            set >::iterator tmp = it;
            it++;
            if(it==rset.end()) break;
            //右扩
            sum+=it->second-tmp->second;
            cost+=max(0,it->first-tmp->second-1);
        }
        if(cost<=m)
            ans=max(ans,sum+m-cost);
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(acm)