HDU 2430 Beans(单调队列)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2430

题意:有n袋豆子(编号1到n),第i袋里有wi的豆子。现在有容量为P的桶,再给出一个常数K(0<=K<P)。要求,从n个袋子中挑出一段连续的袋子,设这些袋子的豆子总量为x,要求在x%P<=K的条件下x/P尽量大?

思路:设sum[i]表示前i个袋子中豆子的总量。则题目转换成选出i,j(1<=i<=j),使得(sum[j]-sum[i])%P<=K且(sum[j]-sum[i])尽量大?设sum[j]=x,sum[i]=y,由0<=(x-y)%p<=K可得0<=(x%p-y%p+p)%p<=K。

(1)若x%p>=y%p,则我们只需要对于x对应的j,在j之前找到一个最小的i使得sum[i]%p<=x%p;

(2)若x%p<y%p,若x%p<=K,则无需找y了(因为不减去y就满足题意);否则,我们可得x%p和y%p属于区间[K+1,P-1],那么(x%p-y%p+p)%p的值大于等于K+1(自己推导一下就知道了。。),这时候也无需找y了(因为找不到。。)。

综上:首先我们将每个sum对P取余,按照余数升序排序,则单调队列中只需要使余数递增即可,每次找队头就是最靠前的y。


 
 struct Node
 {
     int x,pos;
 };
 
 const int MAX=1000005;
 Node a[MAX],Q[MAX];
 int head,tail,n,P,K,pos[MAX],C,num=0;
 __int64 sum[MAX];
 
 int cmp(Node a,Node b)
 {
     if(a.x!=b.x) return a.x<b.x;
     return a.pos<b.pos;
 }
 
 void deal()
 {
     __int64 ans=-1;
     int i,t;
 
     head=tail=0;
     for(i=1;i<=n;i++)
     {
         while(head<tail&&a[i].pos<Q[tail-1].pos) tail--;
         Q[tail++]=a[i];
         while(head<tail&&a[i].x-Q[head].x>K) head++;
         t=a[i].pos;
         if(sum[t]%P<=K) ans=max(ans,sum[t]/P);
         if(head<tail&&Q[head].pos<t)
         {
             ans=max(ans,(sum[t]-sum[Q[head].pos])/P);
         }
     }
     printf("Case %d: %I64d\n",++num,ans);
 }
 
 int main()
 {
     for(scanf("%d",&C);C--;)
     {
         scanf("%d%d%d",&n,&P,&K);
         sum[0]=0;
         int i;
         for(i=1;i<=n;i++)
         {
             scanf("%I64d",&sum[i]);
             sum[i]+=sum[i-1];
             a[i].pos=i;
             a[i].x=sum[i]%P;
         }
         sort(a+1,a+n+1,cmp);
         deal();
     }
     return 0;
 }

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值