zoukankan      html  css  js  c++  java
  • 可并堆试水--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
  • 相关阅读:
    1026 Table Tennis (30)
    1029 Median
    1025 PAT Ranking (25)
    1017 Queueing at Bank (25)
    1014 Waiting in Line (30)
    1057 Stack (30)
    1010 Radix (25)
    1008 Elevator (20)
    字母大小写转换
    Nmap的基础知识
  • 原文地址:https://www.cnblogs.com/Blue233333/p/8266697.html
Copyright © 2011-2022 走看看