可并堆试水--BZOJ1367: [Baltic2004]sequence

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
",ans);
63     return 0;
64 }
View Code
原文地址:https://www.cnblogs.com/Blue233333/p/8266697.html