1. 程式人生 > >BZOJ1367:[Baltic2004]sequence

BZOJ1367:[Baltic2004]sequence

淺談左偏樹:https://www.cnblogs.com/AKMer/p/10246635.html

題目傳送門:https://lydsy.com/JudgeOnline/problem.php?id=1367

顯然,如果給出的陣列是遞增的,那麼答案就是\(0\)

如果給出的陣列是遞減的,根據貪心的思想答案就是\(\sum\limits_{i=1}^{n}|x-t_i|\)\(x\)\(t\)陣列的中位數。

但是給出的陣列是無序的。

我們可以把這個陣列劃成一段段的,每一段都選一個\(x\)去當做中位數。簡單的來講,最優的方案是\(z_i=t_i\)的,但是為了保證\(z\)陣列的遞增性我們必須要把比前一位小的\(z\)

和前一位合併到一段裡去,形成新的一段,然後這一段的\(z\)就是這一段\(t\)的中位數。

但是這樣出現了重複的\(z\),不滿足嚴格遞增的性質。但是我們只需要把\(t_i=t_i-i\)就行了。這樣就預設是嚴格遞增的了。

時間複雜度:\(O(nlogn)\)

空間複雜度:\(O(n)\)

程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e6+5;

ll ans;
int n,tot,cnt;
int l[maxn],r[maxn];
int fa[maxn],dist[maxn],son[maxn][2];
int a[maxn],rt[maxn],val[maxn],siz[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int newnode(int v) {
    val[++tot]=v;
    siz[tot]=1;return tot;
}

int merge(int a,int b) {
    if(!a||!b)return a+b;
    if(val[a]<val[b])swap(a,b);
    son[a][1]=merge(son[a][1],b);
    if(dist[son[a][1]]>dist[son[a][0]])
        swap(son[a][1],son[a][0]);
    dist[a]=dist[son[a][1]]+1;
    siz[a]=siz[son[a][0]]+1+siz[son[a][1]];
    return a;
}

int pop(int u) {
    int res=merge(son[u][0],son[u][1]);
    son[u][0]=son[u][1]=0;
    return res;
}

int main() {
    n=read(),dist[0]=-1;
    for(int i=1;i<=n;i++)
        a[i]=read()-i;
    for(int i=1;i<=n;i++) {
        rt[++cnt]=newnode(a[i]);l[cnt]=r[cnt]=i;
        while(cnt>1&&val[rt[cnt]]<val[rt[cnt-1]]) {
            rt[cnt-1]=merge(rt[cnt-1],rt[cnt]);r[--cnt]=i;
            while(siz[rt[cnt]]>(r[cnt]-l[cnt]+2)/2)rt[cnt]=pop(rt[cnt]);
        }
    }
    for(int i=1;i<=cnt;i++)
        for(int j=l[i];j<=r[i];j++)
            ans+=abs(val[rt[i]]-a[j]);
    printf("%lld\n",ans);
    return 0;
}