BZOJ1367: [Baltic2004]sequence(左偏树)
Description
Input
Output
一个整数R
Sample Input
7
9
4
8
20
14
15
18
9
4
8
20
14
15
18
Sample Output
13
解题思路:
有趣的数学题。
首先确定序列的构造方式。
要求差的绝对值最小,并且递增。
这肯定是照着A序列做的,那么很显然的结论:
若A是递增的,那么Z一定是A序列。
若A是平的,那么Z一定是公差为1的等差数列,中位数为A中的唯一值。
那么就发现了,若保证其非减的话是非常容易得到最优解的。
不断合并中位数即可,原理就是绝对值函数那个好几截棍。(数学课要好好听)
合并中位数可以用可并堆,因为其定义为排名在中间的数,不是不断弹就好了。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define lll l[x].ls 5 #define rrr l[x].rs 6 typedef long long lnt; 7 struct lhnt{ 8 int ls; 9 int rs; 10 int fa; 11 int dis; 12 lnt val; 13 }l[1000000]; 14 struct seg{ 15 int l; 16 int r; 17 int wgt; 18 int root; 19 lnt val; 20 seg(){}; 21 seg(int x,lnt y) 22 { 23 l=r=root=x; 24 wgt=1; 25 val=y; 26 } 27 }st[1000000]; 28 int top; 29 int n; 30 lnt a[1000000]; 31 lnt b[1000000]; 32 int merge(int x,int y) 33 { 34 if(!x||!y) 35 return x+y; 36 if(l[x].val<l[y].val) 37 std::swap(x,y); 38 rrr=merge(rrr,y); 39 l[rrr].fa=x; 40 if(l[rrr].dis>l[lll].dis) 41 std::swap(lll,rrr); 42 l[x].dis=l[rrr].dis+1; 43 return x; 44 } 45 int pop(int x) 46 { 47 return merge(lll,rrr); 48 } 49 int main() 50 { 51 // freopen("a.in","r",stdin); 52 // freopen("my.out","w",stdout); 53 scanf("%d",&n); 54 for(int i=1;i<=n;i++) 55 { 56 scanf("%lld",&a[i]),a[i]-=i; 57 st[++top]=seg(i,a[i]); 58 l[i].val=a[i]; 59 while(top>1&&st[top].val<st[top-1].val) 60 { 61 int x=top-1,y=top; 62 top--; 63 st[x].root=merge(st[x].root,st[y].root); 64 st[x].wgt+=st[y].wgt; 65 st[x].r=st[y].r; 66 while(st[x].wgt>((st[x].r-st[x].l+2)/2)) 67 { 68 st[x].root=pop(st[x].root); 69 st[x].wgt--; 70 } 71 st[top].val=l[st[top].root].val; 72 } 73 } 74 lnt ans=0; 75 for(int i=1;i<=top;i++) 76 for(int j=st[i].l;j<=st[i].r;j++) 77 { 78 b[j]=st[i].val; 79 ans+=std::abs(a[j]-b[j]); 80 } 81 printf("%lld\n",ans); 82 return 0; 83 }