BZOJ 1367 [Baltic2004] sequence(左偏樹)

Problem

Problem 1367. – [Baltic2004]sequence

Solution

這個題目在2005國家集訓隊論文 黃源河 左偏樹的特點及其應用稍微修改後作爲例題出現,具體證明可見論文集[左偏樹的應用]

首先,爲了便於分析,我們將題目轉化
t1,t2,,tn 變爲t11,t22,,tnn ,即可將z 化爲不下降序列

先考慮簡單情況
1. 若序列t 爲不下降序列,那麼當zi=ti 時取到最小值
2. 若序列t 爲單調下降序列,那麼當zi=t 的中位數時取到最小值
情況1非常的顯然,這裏不做過多分析
情況2我們將其轉化爲在平面上有n個點,取一條平行於x 軸的直線使這些點到這條直線的距離和最小

將直線從無限遠的地方緩緩靠近,在接觸到第一個點之前每次移動一個單位距離答案減小n
在接觸到第m 個點是每次移動一個單位距離答案減少nm ,增加m ,共減少n2m
顯然在n>2m 的時候就一直挪好啦
所以將線挪到經過n2 ,也就是取到t 中中位數時,取得最小值
而且因爲t 數組單調下降,所以說要是不使用一條直線,可以將其”掰”成一條直線,前半部分更優了,後半部分也更優了

好的,回到原題
將每個單調下降的部分分成一塊,每一塊都是一個單調下降子序列.當然,某個子序列的長度可能爲1

這些子段的答案都可以處理出來
現在的問題是如何將這些子段答案合併

設子段答案爲x
x1x2 ,這是合法的,可同時去最優解
x1>x2 ,這是非法的,需要合併

這就化爲了我們之前討論過的情況2,任意有斜線的一邊我們都可以將其”掰”回來
那麼最優點就肯定是其中位數啦

我們需要支持取子段中位數,所以用左偏樹來維護即可

代碼如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int dis[N],key[N],ch[N][2],tot,n,cnt;
int arr[N];
int rt[N],l[N],r[N],siz[N];
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') {flag=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {ans=ans*10+ch-'0';ch=getchar();}
    return ans*flag;
}
int merge(int a,int b) {
    if(!a || !b) return a+b;
    if(key[a]<key[b]) swap(a,b);
    ch[a][1]=merge(ch[a][1],b);
    siz[a]=siz[ch[a][0]]+siz[ch[a][1]]+1;
    if(dis[ch[a][1]]>dis[ch[a][0]]) swap(ch[a][0],ch[a][1]);
    dis[a]=dis[ch[a][1]]+1;
    return a;
}
void pop(int &a) {a=merge(ch[a][0],ch[a][1]);}
int create(int x) {
    key[++tot]=x;
    siz[tot]=1;
    dis[tot]=0;
    return tot;
}
int main() {
    n=read();
    memset(dis,-1,sizeof(dis));
    for(int i=1;i<=n;++i) {arr[i]=read()-i;}
    for(int i=1;i<=n;++i) {
        rt[++cnt]=create(arr[i]);
        l[cnt]=r[cnt]=i;
        while(cnt>1 && key[rt[cnt]]<key[rt[cnt-1]]) {
            rt[cnt-1]=merge(rt[cnt-1],rt[cnt]);
            r[cnt-1]=r[cnt];
            --cnt;
            while(siz[rt[cnt]]*2>r[cnt]-l[cnt]+1+1) pop(rt[cnt]);
        }
    }
    long long ans=0;
    for(int i=1;i<=cnt;++i) {
        long long t=key[rt[i]];
        for(int j=l[i];j<=r[i];++j)
            ans+=abs(t-arr[j]);
    }
    printf("%lld\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章