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;

 }

  

 

你可能感兴趣的:(bean)