2019牛客暑期多校訓練營(第七場)C Governing sand 計數排序

題意:n種樹,每種樹有p數量,c砍樹話費,h高度,如果最高的樹的數量大於總樹數量的一半則合法

題解:題目中有一個沒提到的點,會有相同高度的樹,這種情況下得把這些樹的高度加起來,作爲同一種樹(但實際上這些樹的數量和花費是不一樣的,慎重!!!因爲我被卡了好久

首先我們枚舉最高樹的高度作爲對應的,即排一下序從低往高取,1.比他高度高的都刪掉,這裏可以用前綴和處理2.比他低的要刪掉k個使其合法,問題來了,這k個怎麼刪呢,兩種辦法,第一種是維護前k大的和的線段樹,第二種是計數排序(因爲c的範圍只有兩百),我採用了計數排序,每次使用完更新一下計數數組就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define mem(s) memset(s, 0, sizeof(s))
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const int maxn = 1e5+5;
const int mod = 998244353;
struct node {
    ll h,p;
    ll c;
    bool operator <(const node & rhs)const{
        return h<rhs.h;
    } 
}a[maxn];
ll co[maxn],sum[maxn];
int main() 
{
    int n;
    while(scanf("%d",&n)!=EOF){
        memset(co,0,sizeof(co));
        memset(sum,0,sizeof(sum));
        ll ans=1e18;
        for(int i=1;i<=n;i++)
            scanf("%lld%lld%lld",&a[i].h,&a[i].c,&a[i].p);
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+a[i].c*a[i].p;
        }
        ll coun=0;
        for(int i=1;i<=n;i++){
            ll c,cur=i,tmp=0;
            for(int j=i;j<=n&&a[i].h==a[j].h;j++){
                tmp+=a[j].p;
                cur=j;
            }
            c=sum[n]-sum[cur];
            if(tmp*2>coun+tmp){
                //cout<<"!"<<endl;
                ans=min(c,ans);
            }else {
                ll k=coun-tmp+1;
                for(int j=1;j<=200;j++){
                    if(co[j]>=k){
                        c+=k*j;
                        break;
                    }else {
                        k-=co[j];
                        c+=co[j]*j;
                    }
                }
                ans=min(c,ans);
            }
            for(int j=i;j<=cur;j++){
                //tmp+=a[j].p;
                co[a[j].c]+=a[j].p;
                coun+=a[j].p;
            }
            i=cur;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章