1. 程式人生 > >【貪心】【P5078】Tweetuzki 愛軍訓

【貪心】【P5078】Tweetuzki 愛軍訓

Description

Tweetuzki 所在的班級有 \(n\) 名學生,座號從 \(1\)\(n\)。有一次,教官命令班上的 \(n\) 名學生按照座號順序從左到右排成一排站好軍姿,其中 \(1\) 號學生在最左邊,\(n\) 號學生在最右邊。

由於同學們站了很久,怨聲載道,仁慈的教官又不希望大家一起解散導致混亂的局面,於是想了一個辦法:教官從最左邊——也就是 \(1\) 號學生身旁出發,走到 \(n\) 號學生右邊後,再折返回到 \(1\) 號同學旁邊。在教官在從 \(1\) 號同學走到 \(n\) 號同學這段路上,當走到某位同學身邊時,他可以選擇讓這位同學出列,也可以等到折返的時候再使這名同學出列。

但是有一些同學在軍訓過程中表現極壞,因此教官希望他們晚一些出列休息。對於 \(i\) 號同學,定義他的“壞值”為 \(w_i\)。教官希望在一次往返過程中,使得所有學生出列,且最大化 \(\sum_{i=1}^{n}~r_i~\times~w_i\) 的值,其中 \(r_i\) 表示 \(i\) 號同學是第 \(r_i\) 位出列的學生。機智的教官一下子就想出了這個方案,Tweetuzki 大為驚訝,於是他去請教教官如何做到。可是他的膽子很小而且“壞值”很大,教官可能不會告訴他,所以他就找到了你。

Input

第一行一個整數 \(n\) 代表序列長度

第二行是 \(n\) 個整數代表這個序列

Output

第一行一個整數,代表最大壞值

下面一行是 \(n\) 個數,按照出列選手字典序最小輸出壞值

Hint

\(0~\leq~n,w_i~\leq~10^6\)

Solution

考慮答案的出列編號序列一定是單峰的,即如果把序列下標作為 \(x\) 座標, 出列選手的序號作為 \(y\) 座標,則函式影象一定如下

qwq

並且 \(n\) 一定是峰。於是考慮倒著往前決定出隊的順序,考慮已經決定了 \(i+1~\sim~n\)的出隊順序,那麼 \(i\) 只可能放在這條函式圖線的最左側或者最右側,我們比較他們的貢獻。

考慮 \(i\) 放在最左側的情況,這相當於他後面出隊所有人的 \(r\)

值都加一,於是放在左側的貢獻 \(s_l~=~\sum_{j~=i+1}^{n}~w_j~+~w_i\)

同理,考慮放在右側的貢獻,即為他前面已經放的人的個數,即 \(s_r~=~(n-i)~\times~w_i~+~w_i\)

比較 \(sl\)\(s_r\) 的大小即可。由於序號字典序儘可能小,當兩者相等時優先放在左側即可。

Code

#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define putchar(o)\
puts("I am a cheater!")
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
    rg char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    if (ch == '.') {
        ch = IPT::GetChar();
        double base = 1;
        while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    }
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    rg int top=0;
    do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 1000010;

int n;
ll ans, sum;
int MU[maxn], temp[maxn];
std::vector<int>pre,pos;

int main() {
    freopen("1.in", "r", stdin);
    qr(n);
    for (rg int i = 1; i <= n; ++i) qr(MU[i]);
    for (rg int i = n; i; --i) {
        ll sl = sum + MU[i], sr = 1ll * MU[i] * (n - i) + MU[i];
        if (sl >= sr) {pre.push_back(i); ans += sl;}
        else {pos.push_back(i); ans += sr;}
        sum += MU[i];
    }
    qw(ans, '\n', true);
    int sz = pre.size();
    for (rg int i = sz - 1; ~i; --i) qw(MU[pre[i]], ' ', true);
    sz = pos.size();
    for (rg int i = 0; i < sz; ++i) qw(MU[pos[i]], ' ', true);
    putchar('\n');
}