洛谷 P5078 Tweetuzki 愛軍訓
阿新 • • 發佈:2018-12-14
很明顯,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(滑稽