zoukankan      html  css  js  c++  java
  • HDU 2430 Beans (单调队列+公式化简)

    题意:给你n袋豆子,每袋都有w[i]个豆子,接着任选连续任意个袋子的豆子合在一起放入容量为p的多个袋子里(每个袋子必须放满),问剩余的豆子数<=k时,能放满最多的袋子的个数

    题解:个数与p都比较大,直接模拟O(n^2),余数处理(dp)O(n*p)都会超时。

    我们可以首先抽象出一个公式来:设前缀和为sum[i],则我们求(j+1,i)的豆子的数量时会使用sum[i]-sum[j]来求,而我们需要求的就是max(sum[i]-sum[j]),条件的是

    (sum[i]-sum[j])%P<=k ——> (sum[i]%p-sum[j]%p+p)%p<=k

    分类讨论:当sum[i]%p>=sum[j]%p    时就是    sum[i]%p-k <= sum[j]%p <= sum[i]%p

         当sum[i]%p<sum[j]%p我们可以反向来看转化为上面的情况

    则我们枚举sum[i]时就只需要求出最前面一个满足条件的sum[j]就好(因为w[i]非负)

    这样我们找到前缀和求余数数组rem,首先第一关键字rem升序第二关键字下标升序,接着我们枚举rem[现在]时就一定满足rem[现在]>=rem[队首],而rem[现在]-k是非递减的,所以我们可以单调队列来优化,队列里面rem从小到大,当 rem[现在]-k>rem[队首]则出队,最后rem[现在]入队(rem[现在]一定不小于队尾的rem)并向前走到比现在小的下标就好

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define eps 1E-8
    /*注意可能会有输出-0.000*/
    #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
    #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
    #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
    #define mul(a,b) (a<<b)
    #define dir(a,b) (a>>b)
    typedef long long ll;
    typedef unsigned long long ull;
    const int Inf=1<<28;
    const double Pi=acos(-1.0);
    const int Mod=1e9+7;
    const int Max=1000100;
    int que[Max];//单调队列
    struct node
    {
        int rem;//前缀余数
        ll pre;//前缀和
        int pos;
    }res[Max];
    bool cmp(struct node p1,struct node p2)
    {
        if(p1.rem==p2.rem)
        return p1.pos<p2.pos;
        return p1.rem<p2.rem;//排序关键
    }
    ll nmax(ll a,ll b)
    {
        return a>b?a:b;
    }
    ll Solve(int n,int p,int k)
    {
        ll ans=-1ll;
        int top=0,bot=0;
        que[top++]=0;
        for(int i=1;i<=n;++i)//遍历x%p 找最小下标
        {
            while(top>bot&&res[i].rem-k>res[que[bot]].rem)//小于这个值的出队(因为之后这个值会变大)
                bot++;
            if(top>bot)
                ans=nmax(ans,(res[i].pre-res[que[bot]].pre)/(ll)p);
            while(top>bot&&res[que[bot]].pos>res[i].pos)//单调队列维护下标最小
                bot++;
            que[top++]=i;
        }
    
        return ans;
    }
    int main()
    {
        int t,n,p,k,num,coun=0;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d %d %d",&n,&p,&k);
            res[0].rem=0,res[0].pre=0ll,res[0].pos=0;//注意加一个
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&num);
                res[i].pre=res[i-1].pre+num;
                res[i].rem=res[i].pre%p;
                res[i].pos=i;
            }
            sort(res+1,res+n+1,cmp);
            printf("Case %d: %I64d
    ",++coun,Solve(n,p,k));
        }
        return 0;
    }
  • 相关阅读:
    绝对均匀图生成算法
    告别S! S! H! 秒杀终端工具——FastLogin快捷登录
    使用Atom打造无懈可击的Markdown编辑器
    程序异常分析指南
    javascript opacity兼容性随笔
    javascript event兼容性随笔
    javascript Xml兼容性随笔
    addEventListener、attachEvent、cancelBubble兼容性随笔
    算法--逆波兰表达式(数学逆波兰表达式和交并集逆波兰表达式)
    算法--区间数据计算
  • 原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/5908755.html
Copyright © 2011-2022 走看看