1. 程式人生 > >洛谷 P5078 Tweetuzki 愛軍訓

洛谷 P5078 Tweetuzki 愛軍訓

題目連線

很明顯,1e6的範圍,要麼nlgn要麼O(n)

nlgn的話可能會想到藉助一些資料結構,我並沒有想到這種做法

對於這種題,O(n)的做法要麼是線性遞推,要麼就應該是貪心了

考慮這道題我們怎麼貪心

如果可以走無數個來回的話,那很明顯我們可以從小到大依次取出,一定是最大的

可惜只能走一個來回

那麼我們來看看只能走一個來回的話,有什麼特性

對於第i個同學,要麼是去的時候取出,要麼是回來的時候取出,我們來考慮一下這有什麼區別

當第i個同學為從去的時候取出變為回來的時候取出,多做的貢獻就是排名差乘上他的權值

那麼他會對那些同學造成影響呢?由於教官的路線是一個來回,我們不妨破環成鏈來考慮一下。

我們會發現第i個同學對應著兩個位置——\(i\)\(2n-i+1\),設i為去的時候去的時候取,\(2n-i+1\)為回來取,那麼如果我們將去的時候取換成回來取,會造成\([i+1,2n-i]\)之間的點排名整體前移1,也就是說如果第i名同學滯後取出,會造成\(-\sum_{k=i+1}^n w[k]\)的貢獻,這個很明顯可以用字首和或字尾和O(1)算出

那麼很明顯了,如果i同學滯後選擇額外產生的貢獻嚴格大於零(因為要求相同情況序號字典序最小)那麼我們就可以最後再選擇他。

那麼我們就可以直接貪心求方案,用雙指標記錄目前還沒有確認的出列順序的左右端點

沒明白就看程式碼吧

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#ifdef ONLINE_JUDGE
#define printf(o"\n") printf("I AK IOI\n")
#define printf(o) printf("I AK IOI")
#endif
#define ll long long
#define gc getchar
#define maxn 1000005
using namespace std;

inline ll read(){
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}

int n,l,r;
ll ans,a[maxn],b[maxn],c[maxn];
int main(){
    n=read();l=1,r=n;
    for(int i=1;i<=n;++i){
        a[i]=read();
        b[i]=a[i]+b[i-1];    //字首和
    }
    for(int i=1;i<=n;++i){
        ll jia=a[i]*(r-l);    //表示滯後取出產生的正貢獻
        if(jia>b[n]-b[i]){  //大於負貢獻也就是大於零
            c[r]=i;
            ans+=(ll)a[i]*r;
            --r;
        }
        else{    //否則正常取出
            c[l]=i;
            ans+=(ll)a[i]*l;
            ++l;
        }
    }
    printf("%lld\n",ans);
    for(int i=1;i<=n;++i)
        printf("%lld ",a[c[i]]);    //c陣列記錄的只是下標,可別直接輸出c陣列
    return 0;
}

抄題解的猜猜我程式碼能不能AC(滑稽