题目描述

$WTH$获得了一个柱状图,这个柱状图一共有$N$个柱子,最开始第$i$根柱子的高度为$x_i$,他现在要将这个柱状图排成一个屋顶的形状,屋顶的定义如下:
$1.$屋顶存在一个最高的柱子,假设为$i$,最终高度为$h_i$。它是所有柱子之中最高的。
$2.$第$j$根柱子的高度为$h_j=h_i-|i-j|$,但这个高度必须大于$0$,否则就是不合法的。
$WTH$可以对一个柱子做的操作只有将其高度加一或减一,$WTH$正忙着享受自己的人赢生活于是他将把这个柱状图变成屋顶的任务交给了你。你需要求出最少进行多少次操作才能够把这个柱状图变成一个屋顶形状。


输入格式

第一行包含一个正整数$N$。
第二行包含$N$个用空格隔开的正整数,表示$x_i$,含义如题面。


输出格式

输出最少进行多少个操作才能够把这个柱状图变成屋顶的形状。


样例

样例输入:

4
1 1 2 3

样例输出:

3


数据范围与提示

样例解释:

例一升高$2,3,4$号柱子一个单位高度是操作最少的方法之一,最高处为第四个柱子。例二降低第三根柱子三个高度,升高第四个柱子一个高度。最高处为第$2$个柱子。

数据范围:

$30\%$的数据满足:$1\leqslant N\leqslant 100$。
$60\%$的数据满足:$1\leqslant N\leqslant 5,000$。
$100\%$的数据满足:$1\leqslant N\leqslant 100,000$,$1\leqslant h_i\leqslant {10}^9$。


题解

$0\%$算法:

外层循环暴力枚举最高点,中间层暴力枚举最高点的高度,内层循环暴力统计答案,但是因为$h_i$的数据范围,所以理论上讲会$T$到飞起。

时间复杂度:$\Theta(n^2\max h_i)$。

期望得分:$0$分。

实际得分:$15$分。

$60\%$算法:

考虑进行优化,上面的做法肯定不行,但是我们发现,当高度过高或者过低时代价都是很大的,而当高度适中的时候代价才是最小的,这就相当与一个$a<0$的二次函数模型,那么我们就可以对高度进行三分来寻找最小值了。

时间复杂度:$\Theta(n^2\log\max h_i)$。

期望得分:$60$分。

实际得分:$60$分。

$100\%$算法1:

发现枚举最高点已经必不可少,但是统计答案的时候可以进行优化。

假设最高点为$i$,其高度为$h_i$,那么在它左边的点,有$h_j-j=h_i-i$;同理,在它右边的点,有$h_j+j=h_i+i$。

那么问题就变得简单多了,我们可已经所有的点的$h_i+i$和$h_i-i$进行排序,在枚举最高点的时候找到$h_j+j=h_i+i$的那个点所在的位置,之后分别统计两侧的点的个数和这些点的高度和,那么高度和-点的个数$\times h_i-i$的绝对值就是你这个区间之内的答案,而另一侧的区间就是找$(h_i+i)$。

利用两个树状数组进行维护即可降低时间复杂度。

时间复杂度:$\Theta(n\log n\log \max h_i)$。

期望得分:$100$分。

实际得分:$100$分。

$100\%$算法2:

使用模拟退火,代码精简,跑的飞快,但是随机性大,有可能$A$不了,多交几次就好了。

时间复杂度:$\Theta($玄学$)$。

期望得分:$100$分。

实际得分:玄学。


代码时刻

$0\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001],flag[100001];
int minh=1<<30,maxh;
long long ans=200209230020020923;
long long check(int h,int id)
{
long long sum=0;
for(int i=1;i<=n;i++)
{
sum+=abs(flag[i]-h);
if(h-abs(id-i)<=0)
return 200209230020020923;
}
return sum;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
minh=min(minh,a[i]);
maxh=max(maxh,a[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
flag[j]=a[j]+abs(i-j);
for(int j=minh;j<=maxh;j++)ans=min(ans,check(j,i));
}
printf("%lld",ans);
return 0;
}

$100\%$算法1:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[500000];
pair<long long,int> pl[500000],pr[500000];
int rkl[500000],rkr[500000];
long long trl[500000],trr[500000];
int ctl[500000],ctr[500000];
long long ans=1LL<<62;
int lowbit(int x){return x&-x;}
void addl(int x,int w,int c)
{
for(int i=x;i<=n;i+=lowbit(i))
{
trl[i]+=w;
ctl[i]+=c;
}
}
void addr(int x,int w,int c)
{
for(int i=x;i<=n;i+=lowbit(i))
{
trr[i]+=w;
ctr[i]+=c;
}
}
pair<long long,int> suml(int x)
{
long long tmp=0;
int res=0;
for(int i=x;i;i-=lowbit(i))
{
tmp+=trl[i];
res+=ctl[i];
}
return make_pair(tmp,res);
}
pair<long long,int> sumr(int x)
{
long long tmp=0;
int res=0;
for(int i=x;i;i-=lowbit(i))
{
tmp+=trr[i];
res+=ctr[i];
}
return make_pair(tmp,res);
}
inline pair<long long,int> askl(int l,int r)
{
if(l>r)return make_pair(0LL,0);
pair<long long,int> lft=suml(l-1);
pair<long long,int> rht=suml(r);
return make_pair(rht.first-lft.first,rht.second-lft.second);
}
inline pair<long long,int> askr(int l,int r)
{
if(l>r)return make_pair(0LL,0);
pair<long long,int> lft=sumr(l-1);
pair<long long,int> rht=sumr(r);
return make_pair(rht.first-lft.first,rht.second-lft.second);
}
long long wzc(pair<long long,int> p[],int w)
{
int lft=1,rht=n,res;
while(lft<=rht)
{
int mid=(lft+rht)>>1;
if(p[mid].first>w)rht=mid-1;
else{res=mid;lft=mid+1;};
}
return res;
}
inline long long judge(int x,int h)
{
long long res;
int pos;
pair<long long,int> tmp;
res=abs(h-a[x]);
pos=wzc(pl,h-x);
tmp=suml(pos);
res+=1LL*(h-x)*tmp.second-tmp.first;
tmp=askl(pos+1,n);
res-=1LL*(h-x)*tmp.second-tmp.first;
pos=wzc(pr,h+x);
tmp=sumr(pos);
res+=1LL*(h+x)*tmp.second-tmp.first;
tmp=askr(pos+1,n);
res-=1LL*(h+x)*tmp.second-tmp.first;
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pl[i]=make_pair(a[i]-i,i);
pr[i]=make_pair(a[i]+i,i);
}
sort(pl+1,pl+n+1);
sort(pr+1,pr+n+1);
for(int i=1;i<=n;i++)
{
rkl[pl[i].second]=i;
rkr[pr[i].second]=i;
}
for(int i=1;i<=n;i++)
addr(rkr[i],a[i]+i,1);
for(int i=1;i<=n;i++)
{
addr(rkr[i],-a[i]-i,-1);
int lft=max(i,n-i+1),rht=1000000000;
while(rht-lft>1)
{
int mid=(lft+rht)>>1;
int l=judge(i,mid-1);
int r=judge(i,mid);
if(l<r)rht=mid;
else lft=mid;
}
ans=min(ans,min(judge(i,lft),judge(i,rht)));
addl(rkl[i],a[i]-i,1);
}
printf("%lld",ans);
return 0;
}

$100\%$算法2:

#include<bits/stdc++.h>
using namespace std;
int n;
int h[100001],flag[100001];
long long ans=200209230020020923;
long long judge(int x)
{
int mid=(n+1)>>1;
long long cnt=0;
for(int i=1;i<=n;i++)flag[i]=h[i]+abs(x-i);
nth_element(flag+1,flag+mid,flag+n+1);
int val=flag[mid];
val=max(val,max(x,n-x+1));
for(int i=1;i<=n;i++)cnt+=abs(flag[i]-val);
return cnt;
}
int main()
{
srand(time(NULL));
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&h[i]);
double t=1000;
int now=(n+1)>>1;
long long nowans=judge(now);
while(t>1e-5)
{
int tmp=now+(rand()*2-RAND_MAX)*t/1000000;
if(t<1){tmp=max(tmp,1);tmp=min(tmp,n);}
else tmp=(tmp%n+n-1)%n+1;
long long tmpans=judge(tmp);
long long de=tmpans-nowans;
if(de<0||exp(-de/t)*RAND_MAX>rand()){nowans=tmpans;now=tmp;}
ans=min(ans,tmpans);
t*=0.975;
}
printf("%lld",ans);
return 0;
}

rp++

[CSP-S模拟测试]:柱状图(树状数组+二分+三分)的更多相关文章

  1. POJ 2182 Lost Cows 【树状数组+二分】

    题目链接:http://poj.org/problem?id=2182 Lost Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submis ...

  2. The Stream of Corning 2( 权值线段树/(树状数组+二分) )

    题意: 有两种操作:1.在[l,r]上插入一条值为val的线段 2.问p位置上值第k小的线段的值(是否存在) 特别的,询问的时候l和p合起来是一个递增序列 1<=l,r<=1e9:1< ...

  3. POJ 2828 Buy Tickets (线段树 or 树状数组+二分)

    题目链接:http://poj.org/problem?id=2828 题意就是给你n个人,然后每个人按顺序插队,问你最终的顺序是怎么样的. 反过来做就很容易了,从最后一个人开始推,最后一个人位置很容 ...

  4. TZOJ 4602 高桥和低桥(二分或树状数组+二分)

    描述 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”.举 ...

  5. 树状数组+二分||线段树 HDOJ 5493 Queue

    题目传送门 题意:已知每个人的独一无二的身高以及排在他前面或者后面比他高的人数,问身高字典序最小的排法 分析:首先对身高从矮到高排序,那么可以知道每个人有多少人的身高比他高,那么取较小值(k[i], ...

  6. P2161 [SHOI2009]会场预约[线段树/树状数组+二分/STL]

    题目描述 PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个 ...

  7. 牛客多校第3场 J 思维+树状数组+二分

    牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...

  8. 【CSP模拟赛】奇怪的队列(树状数组 &二分&贪心)

    题目描述 nodgd的粉丝太多了,每天都会有很多人排队要签名.  今天有n个人排队,每个人的身高都是一个整数,且互不相同.很不巧,nodgd今天去忙别的事情去了,就只好让这些粉丝们明天再来.同时nod ...

  9. UVA 10909 Lucky Number(树状数组+二分+YY)

    此题测试时预处理等了很久,结果470ms过了...... 题意:开始不怎么懂,结果发现是这个: 波兰裔美国数学家斯塔尼斯拉夫·乌拉姆(Stanislaw Ulam)在20世纪50年代中期开发出了另一种 ...

随机推荐

  1. 如何快速查找到多个字典中的公共键(Key)---Python数据结构与算法相关问题与解决技巧

    如何快速查找到多个字典中的公共键(Key)-?   实际案例: 西班牙足球甲级联赛,每轮球员进球统计: 第1轮: { '苏亚雷斯':1,'梅西':2,'本泽马':1,...} 第2轮: { '苏亚雷斯 ...

  2. 【转载】如何编写ROS的第一个程序hello_world

    目录 1.工作空间的创建 2.功能包的创建 3.功能包的源代码编写 4.功能包的编译配置 5.功能包的编译 6.功能包的启动运行 既然ROS已经成功安装好了,大家一定很想亲自动动手编一个通过起手式例程 ...

  3. [Python3 练习] 002 温度转换2

    题目:温度转换 II (1) 描述 温度的刻画有两个不同体系:摄氏度 (Celsius) 和华氏度 (Fabrenheit) 请编写程序将用户输入的华氏度转换为摄氏度,或将输入的摄氏度转换为华氏度 转 ...

  4. jquery悬停和移出事件

    $('#hides').mouseover(function () { alert("sdfdsf")}).mouseout(function () { alert("啊 ...

  5. Codeforces 429E(欧拉回路)

    题面 传送门 题目大意: 有n条线段,每条线段染红色或蓝色,使得数轴上每个点被红色线段覆盖的次数与被蓝色线段覆盖数差的绝对值小于等于1.输出染色方案. 分析 题意其实可以这样理解: 一段初始全为0 的 ...

  6. iOS 证书(.p12)和描述文件(.mobileprovision)的导出和使用方法

    为什么要导出.p12文件 当我们用大于三个mac设备开发应用时,想要申请新的证书,如果在我们的证书里,包含了3个发布证书,2个开发证书,可以发现再也申请不了开发证书和发布证书了(一般在我们的证书界面中 ...

  7. TensorFlow学习笔记2:逻辑回归实现手写字符识别

    代码比较简单,没啥好说的,就做个记录而已.大致就是现建立graph,再通过session运行即可.需要注意的就是Variable要先初始化再使用. import tensorflow as tf fr ...

  8. 7——C++类的使用

     定义了一个类之后,便可以如同用int.double等类型符声明简单变量一样,创建该类的对象,称为类的实例化.           类的定义实际上是定义了一种类型,类不接收或存储具体的值,只作为生成具 ...

  9. keepalived的配置文件

    ! Configuration File for keepalived global_defs { notification_email { # acassen@firewall.loc # fail ...

  10. python的list内存分配算法

    前提:python为了提高效率会为list预先分配一定的内存空间供其使用,避免在每次append等操作都去申请内存,下面简单分析下list的内存分配算法,主要就是两段. 1.当没有元素时,newsiz ...