n<=1e6个数,把他们修改成递增序列需把每个数增加或减少的总量最小是多少?
方法一:可以证明最后修改的每个数一定是原序列中的数!于是$n^2$DP(逃)
方法二:把$A_i$改成$A_i-i$,变论文题:论文
大概证明是这样的:考虑合并两个区间的答案,假如一个区间答案是{u,u,u,……,u},另一个是{v,v,v,……,v},那合并之后,如果u<=v最优就{u,u,……,u,v,……,v};如果u>v,假设最优是
{b1,b2,……,bn,bn+1,……,bm},那么一定有bn<=u,否则把前半部分改成{u,u,……,u}不会更差。同理bn+1>=v。
这里有个不懂的地方:
因此可以把他改成{bn,bn,……,bn,bn+1,……,bn+1},不会更差。可以用几何意义感性理解一下:左边离u越近越优,右边离v越近越优。
然后由于bn<=u,bn+1>=v,本着“bn能大就大,bn+1能小就小”的原则让bn=bn+1。于是合并后的最优解为{w,w,w,……,w},最优的w是谁呢?肯定是整个区间的中位数啦。
然后就可以可并堆做一波合并了,因为这里合并后中位数只会变小,可以维护一个区间的一半小的数或一半大的数,合并两个区间时,如果两个区间大小都是奇数,则堆里会多出一个数,删之。
可并堆常需合并特定点所在的堆,因此常与并查集连用。千万别并查集懵逼了!!因为并查集操作失误调了一晚上。。
1 #include<string.h> 2 #include<stdlib.h> 3 #include<stdio.h> 4 #include<math.h> 5 //#include<assert.h> 6 #include<algorithm> 7 //#include<iostream> 8 using namespace std; 9 10 int n; 11 #define maxn 1000011 12 int root[maxn]; 13 int find(int x) {return x==root[x]?x:(root[x]=find(root[x]));} 14 struct leftist 15 { 16 struct Node 17 { 18 int v,ls,rs,dis,size; 19 }a[maxn]; 20 int size; 21 leftist() {a[0].dis=-1;} 22 int merge(int x,int y) 23 { 24 if (!x || !y) return x^y; 25 if (a[x].v<a[y].v) {int t=x; x=y; y=t;} 26 a[x].rs=merge(a[x].rs,y); 27 if (a[a[x].ls].dis<a[a[x].rs].dis) {int t=a[x].ls; a[x].ls=a[x].rs; a[x].rs=t;} 28 a[x].dis=a[a[x].rs].dis+1; 29 a[x].size=a[a[x].ls].size+a[a[x].rs].size+1; 30 return x; 31 } 32 void push(int x,int &root,int val) 33 { 34 a[x].v=val; a[x].ls=a[x].rs=a[x].dis=0; a[x].size=1; 35 root=merge(root,x); 36 } 37 int top(int root) {return a[root].v;} 38 void pop(int &root) {root=merge(a[root].ls,a[root].rs);} 39 }q; 40 41 int a[maxn],sta[maxn],die[maxn],top; 42 int main() 43 { 44 scanf("%d",&n); 45 for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]-=i; 46 for (int i=1;i<=n;i++) 47 { 48 q.push(i,root[i],a[i]); int x,y; 49 while (top && q.top(x=find(root[sta[top]]))>q.top(y=find(root[i]))) 50 { 51 bool flag=0; 52 if (((sta[top]-sta[top-1])&1) && ((i-sta[top])&1)) flag=1; 53 root[x]=root[y]=q.merge(x,y); x=root[x]; 54 if (flag) die[x]=1,q.pop(root[x]),root[root[x]]=root[x]; 55 top--; 56 } 57 sta[++top]=i; 58 } 59 #define LL long long 60 LL ans=0; 61 for (int i=1;i<=n;i++) ans+=fabs(a[i]-q.top(find(root[i]))); 62 printf("%lld\n",ans); 63 return 0; 64 }