• 憨豆先生卖豆子(好题)(思维线段树)(经典技巧)


    题意:给定n,p,k   n个数,然后取连续的一段,让着一段的sum%p<=k,然后求满足条件的最大sum/p

    思路:见注释

    /*
        取一段[L,R]让这个区间和取模后小于等于k 
        切记,取模操作符合减法的分配定律
        条件可以转化为(sum-pre[L]-suf[R])%p<=k  ===>(sum%p-pre[L]%p-suf[R]%p<=k
        
        方法就是枚举这个pre[L]%p,然后可以借此确定suf[R]%mod的最右边的位置,更新最大值即可 
     
    */
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pb push_back
    #define lson root<<1,l,midd
    #define rson root<<1|1,midd+1,r
    const int M=1e6+6;
    ll tree[M<<2],w[M],suf[M],pre[M],prel[M],sufr[M];
    void up(int root){
        tree[root]=max(tree[root<<1],tree[root<<1|1]);
    }
    void build(int root,int l,int r){
        if(l==r){
            tree[root]=sufr[l];
            return ;
        }
        int midd=(l+r)>>1;
        build(lson);
        build(rson);
        up(root);
    }
    ll query(int L,int R,int root,int l,int r){
        if(L<=l&&r<=R){
            return tree[root];
        } 
        int midd=(l+r)>>1;
        ll res=-2;
        if(L<=midd)
            res=max(res,query(L,R,lson));
        if(R>midd)
            res=max(res,query(L,R,rson));
        return res;
    }
    ll p;
    ll mm(ll x){
        if(x<0)
            x+=p;
        return x%p;
    } 
    int main(){
        int t;
        scanf("%d",&t);
        for(int sign=1;sign<=t;sign++){
            ll n,k,ans=-1;
            scanf("%lld%lld%lld",&n,&p,&k);
            for(int i=0;i<=p;i++)
                prel[i]=sufr[i]=-1;
            for(int i=0;i<=2*p;i++)
                tree[i]=0;
            for(int i=1;i<=n;i++){
                scanf("%lld",&w[i]);
                pre[i]=pre[i-1]+w[i];///前缀和 
                ll premod=pre[i]%p;
                if(prel[premod]==-1)///记录前缀和mod意义下,从左往右第一次出现的位置, 
                    prel[premod]=i;
                if(premod<=k)
                    ans=max(pre[i]/p,ans);
            }
            printf("Case %d: ",sign);
            ll summod=pre[n]%p;///总的前缀和取模 
            if(summod<=k){///全部加起来满足条件 
                printf("%lld
    ",pre[n]/p);
                continue;
            }
            suf[n+1]=0;
            for(int i=n;i>=1;i--){
                suf[i]=suf[i+1]+w[i];
                ll sufmod=suf[i]%p;
                if(sufr[sufmod]==-1)
                    sufr[sufmod]=i;
                if(sufmod<=k)
                    ans=max(ans,suf[i]/p);
            }
            build(1,1,p);///依照sufr来做线段树,下标是后缀和取模,tree存的是位置  
            for(int i=1;i<p;i++){///枚举从左开始的模p意义下的前缀和 
                if(prel[i]==-1)
                    continue;
                int temp=summod-i;///总的减去左边 
                int r=mm(temp);
                int l=mm(temp-k);
                if(l==0||l>r)///查找区间不合法 
                    continue;
                int pos=query(l,r,1,1,p);//返回满足条件的最大值,即最后的坐标
                
                if(pos<=prel[i]+1)///因为至少要取一个袋子,所以如果pos==prel[i]+1,那么利用下面步骤的处理就会选到0个袋子,所以不可取 
                    continue; 
                ll x=pre[n]-pre[prel[i]]-suf[pos];
                ans=max(ans,x/p);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    linux批量远程多服务器FTP并下载文件的脚本
    NPM更换国内源
    Win10禁用无用的服务
    JS测试
    FastAdmin导出
    VScode全局设置
    Vue路由history模式
    kill_devtmpfsi
    获取域名URL
    Axios去除Respones中的config、headers、request
  • 原文地址:https://www.cnblogs.com/starve/p/11873876.html
Copyright © 2020-2023  润新知