zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校(八)

    K Kabaleo Lite(前缀和+高精度(大数处理))

    (1)把一个超过long long范围的数字拆成两个long long存储!

    (2)计算前缀和的操作非常聪明

    代码来自:

    https://blog.nowcoder.net/n/3b2e46a2f0ff4201b1587b1cffb08d6d

    #include<bits/stdc++.h>
    using namespace std;
    const long long N=1e5+9,mod=1e14;                  //控制 m 在1e14范围之内 
    long long a[N],b[N],s[N],n,m,ans;
    int main()
    {
        int T,l,r,i,j,k;
        scanf("%d",&T);
        for(k=1;k<=T;k++)
        {
            memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(s,0,sizeof(s));
            scanf("%lld",&n);
            for(i=0;i<n;i++) scanf("%lld",&a[i]);
            scanf("%lld",&b[0]);
            for(i=1;i<n;i++) scanf("%lld",&b[i]),b[i]=min(b[i],b[i-1]);        //b:个数 
            s[0]=a[0];
            for(i=1;i<n;i++) s[i]=s[i-1]+a[i];                               //s:前缀和  
            l=r=ans=0,m=s[l]*b[l];                                    //以上均从0开始计数 
            while(r<n)                      //遍历
            {
                while(r<n && s[r]<=s[l]) r++;
                if(r==n)break;                            //选取第一个前缀和大于当前的位置 
                m+=b[r]*(s[r]-s[l]),ans+=m/mod,m%=mod;          //m += b[r] * (s[r] - s[l])  加上这一段的数目
                l=r;                                    //更新 l
            }
            printf("Case #%d: %lld ",k,b[0]);
            if(ans) printf("%lld%014lld
    ",ans,m);
            else printf("%lld
    ",m);
        }
    }

    略为暴力的作法:

    (1)用priority队列从大到小储存前缀和

    (2)从前缀和大到小遍历,减去当前的全部个数,下一个前缀和如果没有这么多个数就等于被减完了,跳过,有的话就算上多出来的部分。cnt一直在增加,相当于减掉的越来越多

    (3)用__int128储存大数

    代码来自:

    https://blog.nowcoder.net/n/6cecfd81e1e3465aa75b2b82e29973e9

    #include<bits/stdc++.h>
    #define ll __int128
    #define pii pair<ll,pair<ll,ll> >
    using namespace std;
     
    inline __int128 read(){
        __int128 x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')
                f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';
            ch=getchar();
        }
        return x*f;
    }
     
    inline void print(__int128 x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>9)
            print(x/10);
        putchar(x%10+'0');
    }
     
    const ll maxn=1e5+10;
    ll a[maxn],b[maxn],c[maxn];
    priority_queue<pii> q;
     
    int main()
    {
     
        ll t;
        t=read();
        ll kk=1;
        while(t--){
            ll n;
            n=read();
            for(ll i=0;i<n;i++) a[i]=read();
            for(ll i=0;i<n;i++) b[i]=read();
            for(ll i=0;i<n;i++){
                if(i) c[i]=c[i-1]+a[i];
                else c[i]=a[i];
            }
            __int128 ans=0;
            ll bb=b[0];
            for(ll i=0;i<n;i++){
                bb=min(bb,b[i]);
                q.push({c[i],{bb,i}});
            }
            ll cnt=0,pos=n;
            while(!q.empty()){
                ll x=q.top().first,y=q.top().second.first,z=q.top().second.second;
                q.pop();
                if(y<=cnt||pos<=z) continue;
                ans+=x*(y-cnt);
                cnt=y;
                pos=z;
            }
            cout<<"Case #";
            print(kk++);
            cout<<": ";
            print(b[0]);
            cout<<" ";
            print(ans);
            cout<<endl;
        }
        return 0;
    }

    最为暴力的作法:

    (1)也是把前缀和数组从大到小排序

    (2)用线段树全部区间修改个数

    这就是我的代码orz

    #include<bits/stdc++.h>
    using namespace std;
    #define ll __int128
    //区间修改
    //点查询
    //线段树记录每个序号的数目
    const int inf=1e9+1e5;
    struct __Sum{
        ll sum;
        int id;
    } Sum[100004];
    ll num[100004<<2],add[100004<<2];
    ll mi=inf;
    int first,vis;
    void push_up(int rt){
        num[rt]=num[rt<<1]+num[rt<<1|1];
    }
    void push_down(int rt,int m){
        if(add[rt]){
            add[rt<<1]+=add[rt];
            add[rt<<1|1]+=add[rt];
            num[rt<<1]+=(m-(m>>1))*add[rt];
            num[rt<<1|1]+=(m>>1)*add[rt];
            add[rt]=0;
        }
    }
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    void build(int l,int r,int rt){
        add[rt]=0;
        if(l==r){
            scanf("%lld",&num[rt]);
            if(vis==0){first=num[rt];vis=1;}
            mi=min(mi,num[rt]);
            num[rt]=mi;
            return;
        }
        int mid=(l+r)>>1;
        build(lson);
        build(rson);
        push_up(rt);
    }
    void update(int a,int b,int c,int l,int r,int rt){
        if(a<=l&&b>=r){
            num[rt]+=(r-l+1)*c;
            add[rt]+=c;
            return;
        }
        push_down(rt,r-l+1);
        int mid=(l+r)>>1;
        if(a<=mid) update(a,b,c,lson);
        if(b>mid) update(a,b,c,rson);
        push_up(rt);
    }
    int query(int a,int b,int l,int r,int rt){
        if(a<=l&&b>=r) return num[rt];
        push_down(rt,r-l+1);
        int mid=(l+r)>>1;
        int ans=0;
        if(a<=mid) ans+=query(a,b,lson);
        if(b>mid) ans+=query(a,b,rson);
        return ans;
    }
    bool cmp(__Sum a,__Sum b){
        return a.sum>b.sum;
    }
    void init(){
        memset(num,0,sizeof(num));
        memset(add,0,sizeof(add));
        memset(Sum,0,sizeof(Sum));
        mi=inf;
        Sum[0].sum=0;
        vis=0;
    }
    inline void print(__int128 x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>9)
            print(x/10);
        putchar(x%10+'0');
    }
    int main(){
        int t;
        scanf("%d",&t);
        for(int c=1;c<=t;++c){
            int n;
            scanf("%d",&n);
            init();
            int tmp;
            for(int i=1;i<=n;++i){
                scanf("%d",&tmp);
                Sum[i].sum=tmp+Sum[i-1].sum;
                Sum[i].id=i;
            }
            build(1,n,1);
            sort(Sum+1,Sum+1+n,cmp);
            int cnt=0;
            __int128 ans=0;
            //num[1]盘,那么计算num[1]次前缀和即可
            for(int i=1;i<=n;++i){
                int tmp=query(Sum[i].id,Sum[i].id,1,n,1);
                if(tmp<=0) continue;
                //当前的数目
                //前面扣除为0之后,后面也要减掉这个值。
                if(cnt+tmp>=first){
                    tmp=first-cnt;
                    ans+=(ll)tmp*Sum[i].sum;
                    break;
                }
                cnt+=tmp;
                ans+=(ll)tmp*Sum[i].sum;
                update(1,n,-tmp,1,n,1);
                //1到n都剪掉当前的数目
            }
            printf("Case #%d: %d ",c,first);
            print(ans);
            printf("
    ");
        }
    }
  • 相关阅读:
    uni-app 版本更新控制
    Chrome为什么打开一个页面,会有4个进程?
    vue 导航栏滚动吸顶
    vue 运行后, sass 报错
    HAProxy——HAProxy、Nginx、LVS优势劣势 (转)
    HAProxy——关于配置的备份
    RabbitMQ——用HAProxy实现负载均衡
    docker——Docker常见问题总结 (转)
    docker——docker容器内存和CPU使用限制
    docker——用docker-compose安装redis
  • 原文地址:https://www.cnblogs.com/rainartist/p/13431834.html
Copyright © 2011-2022 走看看