题面:BZOJ传送门
题目大意:给你一个序列$a$,让你构造一个递增序列$b$,使得$sum |a_{i}-b_{i}|$最小,$a_{i},b_{i}$均为整数
神仙题..
我们先考虑b不递减的情况
假设现在有一段单调的序列$A$
如果$A$是递增的,显然$b[i]=a[i]$是最优解
如果$A$是递减的,$b$的每一项=序列$A$的中位数时是最优解
简单证明一下递减的情况:
1.序列$A$元素数量是奇数时,我们以中位数为对称轴,那么对称的两个数带来的贡献就是它们的差值,而中位数本身不会产生贡献,如果选取的不是中位数,必然会导致中位数产生贡献,而且对称的两个数还可能产生差值以外的贡献
2.序列$A$元素数量是偶数时,选取的数在中间的两个数之间即可,贡献都是一样的
我们如何利用这一性质呢?
我们把序列拆分成很多递减的段,递增子段的每一个数都是单独的一段
我们的目的是保证$b$不递减,即把每一段取的数画成一个函数来看是不递减的
每次我们在序列末尾加入一个数$a_{i}$,都看看这一段的中位数是否$geq $前面一段的中位数,不满足就和前一段合并,然后依次重复此过程
为什么答案合并后不会变差?太弱了并不会证..感性理解一下吧。因为这两段原来取的就是最优解,但并不满足要求,我们也只能把两段合并,再用相同的方法求最优解了..
具体实现可以用左偏树维护大根堆,记录每一段较大的$left lceil frac{n}{2} ight ceil$个数,当两个奇数段合并时删除一次堆顶即可,记录每一段的信息可以用结构体
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 1000010 6 #define ll long long 7 using namespace std; 8 const ll inf=0x3f3f3f3f3f3f3f3fll; 9 10 template <typename _T> void read(_T &ret) 11 { 12 ret=0; _T fh=1; char c=getchar(); 13 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 14 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 15 ret=ret*fh; 16 } 17 18 struct Heap{ 19 int fa[N1],ch[N1][2],h[N1]; ll val[N1]; 20 int merge(int x,int y) 21 { 22 if(!x||!y) return x+y; 23 if(val[x]<val[y]) swap(x,y); 24 //pushdown(x); 25 ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x; 26 if(h[ch[x][0]]<h[ch[x][1]]) swap(ch[x][0],ch[x][1]); 27 h[x]=h[ch[x][1]]+1; 28 return x; 29 } 30 int del(int x) 31 { 32 fa[ch[x][0]]=fa[ch[x][1]]=0; 33 int y=merge(ch[x][0],ch[x][1]); 34 ch[x][0]=ch[x][1]=0; 35 return y; 36 } 37 }h; 38 39 ll a[N1]; 40 struct node{int l,r,x;}; 41 node stk[N1]; int n,tp; 42 43 int main() 44 { 45 scanf("%d",&n); 46 int i,j,x,y,len; node K; ll ans=0,tmp; 47 for(i=1;i<=n;i++) read(a[i]); 48 for(i=1;i<=n;i++) 49 { 50 h.val[i]=a[i]-i; x=i; len=1; 51 while(tp) 52 { 53 K=stk[tp]; 54 if(h.val[x]>=h.val[K.x]) break; 55 x=h.merge(x,K.x); tp--; 56 if( (len&1) && ((K.r-K.l+1)&1) ) x=h.del(x); 57 len+=K.r-K.l+1; 58 } 59 stk[++tp]=(node){i-len+1,i,x}; 60 } 61 for(i=1;i<=tp;i++) 62 for(j=stk[i].l;j<=stk[i].r;j++) 63 { 64 tmp=a[j]-(h.val[stk[i].x]+j); 65 ans+=((tmp>0)?tmp:-tmp); 66 } 67 printf("%lld ",ans); 68 return 0; 69 }