2019暑假集训8.22(problem2.dinner)(二分)

Posted yyys-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019暑假集训8.22(problem2.dinner)(二分)相关的知识,希望对你有一定的参考价值。

技术图片

技术图片

因为求最大时间的最小,考虑到二分答案。

常规思路:断环为链,二倍链。

最暴力的做法是在n个位置都断一次环,序列for一遍暴力分组,大于mid了就分到下一组,时间O(n^2 logn)

考虑优化:

我们发现每一次暴力分组是把整个序列都给走了一遍,分好的组满足和<=mid,与其一个个的把值加到和里,不如先处理出前缀和,然后对于每一个组的起点,二分找出这个组的终点(我代码中是用upper_bound实现的)

int s=t[q],c=1;//s即为起点
int flagg=0;
for(int i=q;i<=q+n-1;++i)

    if(c>mid)flagg=1;break;
    if(t[i]>mid)return 1;
    int v=mid-s;//还有这么多才到mid 
    v+=sum[i];//在前缀和数组中查找,所以加上sum[i] 
    int pos=upper_bound(sum+i,sum+2*n+1,v)-sum;//在i之后进行寻找 
    if(pos>q+n-1)break;
    i=pos-1;//循环完后还要++i,所以这里i=pos-1 
    if(t[pos]<=mid)s=t[pos],c++;
    else return 1;

然鹅这样最后的点还是会T,再考虑优化

我们在每次的新断的链进行操作前加了一句这样的话:

if(sum[q]>mid)return 1;

即起点的前缀和小于mid的话,这样断开的链一定是不满足的。

为什么?

我们看这样一组数据

技术图片

假如二分的答案mid=11

试着模拟一下算法过程,断的链分别是

1 5 2 7 8

5 2 7 8 1

2 7 8 1 5

到7 8 1 5 2这一组时一定是不满足的(假设前面的也不满足所以一直向后走)

因为1 5 2 7 8是不满足的(且1 5 2 分为了一组),所以才会往后断环,到了7 8 1 5 2 仍是会1 5 2 分为一组还是不满足,就相当于是把1 5 2 7 8 分成的组调了一下顺序而已。

技术图片
#include<bits/stdc++.h>
#define N 50003
#define M 3003
#define INF 2100000000
using namespace std;
int read()

    int x=0,f=1;char s=getchar();
    while(s<0||s>9)if(s==-)f=-1;s=getchar();
    while(s>=0&&s<=9)x=x*10+s-0;s=getchar();
    return x*f;

int t[2*N],sum[N*2],n,m;
int check(int mid)

    int q=1;
    while(q<=n)
    
        if(sum[q]>mid)return 1;//!!!当前面的前缀和>mid还不成功,这种情况在开始枚举时已经失败,于是此mid不成立
        int s=t[q],c=1;//s即为起点
        int flagg=0;
        for(int i=q;i<=q+n-1;++i)
        
            if(c>mid)flagg=1;break;
            if(t[i]>mid)return 1;
            int v=mid-s;//还有这么多才到mid 
            v+=sum[i];//在前缀和数组中查找,所以加上sum[i] 
            int pos=upper_bound(sum+i,sum+2*n+1,v)-sum;//在i之后进行寻找 
            if(pos>q+n-1)break;
            i=pos-1;//循环完后还要++i,所以这里i=pos-1 
            if(t[pos]<=mid)s=t[pos],c++;
            else return 1;
        
        if(flagg==0&&c<=m&&s<=mid)return 0;
        q++;//重新断环 
    
    return 1;

int main()

//    freopen("dinner.in","r",stdin);
  //  freopen("dinner.out","w",stdout);
    n=read(),m=read();
    int l=0,r=0;
    for(int i=1;i<=n;++i)
    
        t[i]=read();
        sum[i]=sum[i-1]+t[i];
        r+=t[i];
    
    for(int i=n+1;i<=2*n;++i)
      t[i]=t[i-n],sum[i]=sum[i-1]+t[i];
    int ans;
    while(l<r)
    
        int mid=(l+r)>>1;
        if(check(mid))l=mid+1;
        else ans=mid,r=mid;
    
    printf("%d\\n",ans);
 
/*
5 5
1 2 5 4 3

5 2
1 5 2 7 8

10 4
1 3 5 2 7 8 2 1 2 4
*/
View Code

 也还有倍增的做法

技术图片

 

以上是关于2019暑假集训8.22(problem2.dinner)(二分)的主要内容,如果未能解决你的问题,请参考以下文章

2019暑假集训 8/16

杭电多校2019.7.24--暑假集训

2019暑假集训 8/2

2019暑假集训 数字游戏

2019暑假集训 Intervals

CSU-ACM2019暑假集训