所求的Z序列为6,7,8,13,14,15,18.
R=13
左偏树。
详细题解(在P13)。
这个题解求的z是不减的,要求单增的话只要把a[i]减i再算即可。
很巧妙的是用左偏树来合并两个区间的中位数。
根据题解,前一个区间的中位数大于后一个区间的中位数,合并之后中位数只会变小,因此先把两个对合并(大根堆),然后把最大的一个个除去,最后剩下一半即可,最大的那个就是中位数。
一定要注意,这里的“一半”一定要向上取整!!因为如果两个区间原本都是3个,向下取整都变成1个,合并之后6个只剩下2个了。。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #define M 1050000 using namespace std; int n,a[M],root[M],l[M],r[M],w[M],tot=0,cnt=0; struct Ltree { int size,l,r,dis,v; }t[M]; void read(int &tmp) { tmp=0; char ch=getchar(); int fu=1; for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') fu=-1; for (;ch>='0'&&ch<='9';ch=getchar()) tmp=tmp*10+ch-'0'; tmp*=fu; } int New_heap(int x) { t[x].v=a[x]; t[x].l=t[x].r=t[x].dis=0; t[x].size=1; return x; } int Merge(int x,int y) { if ((!x)||(!y)) return x+y; if (t[x].v<t[y].v) swap(x,y); t[x].size+=t[y].size; t[x].r=Merge(t[x].r,y); if (t[t[x].l].dis<t[t[x].r].dis) swap(t[x].l,t[x].r); t[x].dis=t[t[x].r].dis+1; return x; } int Del(int x) { return Merge(t[x].l,t[x].r); } int main() { read(n); for (int i=1;i<=n;i++) read(a[i]),a[i]-=i; tot=0; t[0].dis=-1; for (int i=1;i<=n;i++) { root[++tot]=New_heap(i); w[tot]=a[i]; l[tot]=r[tot]=i; while (tot>1&&w[tot-1]>w[tot]) { tot--; r[tot]=r[tot+1]; root[tot]=Merge(root[tot],root[tot+1]); int x=(r[tot]-l[tot]+2)/2; while (t[root[tot]].size>x) root[tot]=Del(root[tot]); w[tot]=t[root[tot]].v; } } long long ans=0LL; for (int i=1;i<=tot;i++) for (int j=l[i];j<=r[i];j++) ans+=abs(a[j]-w[i]); printf("%lld\n",ans); return 0; }
【BZOJ 1367】 [Baltic2004]sequence
原文地址:http://blog.csdn.net/regina8023/article/details/43958073