zoukankan      html  css  js  c++  java
  • 枚举+树状数组(经典)

    题意:有n种树,给出每种数的高度、移除的花费和数量,求最小花费是多少使得剩下树中最高的树的数量占一半以上。

    分析:以高度排序,枚举每一高度,对当前枚举到的这个高度视为最高高度,数目由num棵,然后后面肯定要砍掉因为比他高,然后再考虑比他小的,因为要构成俩倍的关系,所以最多保留num-1棵。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=205;
    const int M=1e5+5;
    ll tree[N][2];
    struct node{
        ll h,p,c;
    }a[M];
    ll sufc[M],sufp[M];
    bool cmp(node p,node q){
        return p.h<q.h;
    }
    void add(int u,ll x,int i){
        while(u<=200){
            tree[u][i]+=x;
            u+=-u&u;
        }
    }
    ll SUM(int u,int i){
        ll ans=0ll;
        while(u){
            ans+=tree[u][i];
            u-=-u&u;
        }
        return ans;
    }
    int main(){
        int n;
        while(~scanf("%d",&n)){
            memset(tree,0ll,sizeof(tree));
            ll sum=0,maxc=0,minc=M;
            for(int i=1;i<=n;i++){
                scanf("%lld%lld%lld",&a[i].h,&a[i].c,&a[i].p);
                sum+=a[i].p;
                maxc=max(maxc,a[i].c);
                minc=min(minc,a[i].c);
            }
            sort(a+1,a+1+n,cmp);
            for(int i=n;i>=1;i--){
                sufc[i]=sufc[i+1]+a[i].p*a[i].c;//后缀代价总和
                sufp[i]=sufp[i+1]+a[i].p;//后缀树木总数和 
            }
            ll ans=sufc[1];
            int i=1;
            /*for(int i=1;i<=n;i++)
                cout<<sufc[i]<<" ";
            cout<<endl;*/
            while(i<=n){
                int l=i,r=i;
                ll num=0ll,needcost=0ll;
                while(r<=n&&a[r].h==a[l].h)
                    num+=a[r++].p;
                
                needcost+=sufc[r];
                ll need=sum-num-sufp[r]-(num-1);//枚举到当前认定为最高高度,比当前高的肯定要砍掉,然后再考虑前面因为要俩倍,所以最多砍掉num-1棵树 
                if(need<=0)
                    ans=min(ans,needcost);
                else{
                    ll L=minc,R=maxc,s=0ll;
                    while(L<=R){
                        ll midd=(L+R)>>1;
                        if(SUM(midd,0)>=need)
                            s=midd,R=midd-1;
                        else
                            L=midd+1;
                    }
                    need-=SUM(s,0);
                    needcost+=SUM(s,1);
                    //因为要保持贪心,所以出现前缀和大于要砍的树的时候要减掉多出来的代价 
                    needcost-=s*abs(need);
                }
                ans=min(ans,needcost); 
                while(l<r){
                    add(a[l].c,a[l].p,0);
                    add(a[l].c,a[l].c*a[l].p,1);
                    l++;
                }
                i=r;//cout<<r<<endl;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    docker 容器启动初始化,centos镜像启动并执行
    odoo 分布式session 失效解决方案
    文件分布式存储 minio docker
    odoo reports 报表打印pdf初探
    odoo 分布式快速更新
    linux Warning: Stopping docker.service, but it can still be activated by:
    linux 查看80端口的连接数
    css flex 涨姿势了
    odoo 后台打印日志修改
    iOS 导航栏消失
  • 原文地址:https://www.cnblogs.com/starve/p/11330365.html
Copyright © 2011-2022 走看看