• 可并堆试水--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
  • 相关阅读:
    关于maven下载速度慢,下载完的依赖包不知去向的应对措施
    进一步解析二分搜索树的实现
    mysql语法建库建表综合整理是示例
    走进二分搜索树的第一课
    优先队列和堆
    window系统mysql安装后获取默认密码
    微信小程序中使用云开发获取openid
    使用IDEA将springboot框架导入的两种方法
    C/C++、Qt4实现FTP客户端(有无界面版)
    云服务器搭建代理服务器 —— 某sock ,简单说明
  • 原文地址:https://www.cnblogs.com/Blue233333/p/8266697.html
Copyright © 2020-2023  润新知