BZOJ1367:[Baltic2004]sequence
阿新 • • 發佈:2019-01-10
淺談左偏樹: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_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; }