题目描述

$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. 06 使用bbed修复update的数据--01

    场景1 表t3 SQL> select * from t3; ID NAME ---------- -------------------- aaa bbbb SQL> update t3 ...

  2. 你还没有真正理解的innodb_flush_log_at_trx_commit

    关于innodb_flush_log_at_trx_commit的描述,看了mysql手册中的解释,感觉都不够清晰明了,下面试图以最简单直白的方式解释一下innodb_flush_log_at_trx ...

  3. Node.js实战11:fs模块初探。

    fs模块封装了对文件操作的各种方法,比如同步和异步读写.批量操作.流.监听. 我们还是通常例程学习, 获取目录下的文件清单: var fs =require("fs"); fs.r ...

  4. [Python3] 015 冰冻集合的内置方法

    目录 0. 前言 英文名 元素要求 使用限制 返回 方法数量 1. 如何查看 frozenset() 的内置方法 2. 少废话,上例子 2.1 copy() 2.2 difference() 2.3 ...

  5. Java 13 发布了!

    点击上方蓝色链接,关注并"设为星标" Java干货,每天及时推送 通告一下,Java 13 09/17 发布了,尝鲜地址: https://www.oracle.com/techn ...

  6. JS相比TS的缺点(或TS的优点)

    JS只有在运行时,才会抛出错误, JS里有任何的拼写错误 都不会提示错误 JS运行时报的错,指向也未必是错误的源头,也就是说:A类里因为书写代码有误,会造成运行时其他的类报错如B JS无法做到不同pa ...

  7. centos解决Could not find a version that satisfies the requirement pip3 (from versions: none)及No matching distribution found for pip3问题

    python环境:python 3.8 报错信息: WARNING: pip is configured with locations that require TLS/SSL, however th ...

  8. ofbiz idea 启动

    1.下载gradle并安装到本地 2.idea引入gradle 3.gradle右键选择refresh,项目会重新编译并加载gradle的task 4.可以再编译一下 5.没问题的话打开,jar ap ...

  9. 处理键盘事件 禁止后退键(Backspace)密码或单行、多行文本框除外

    //处理键盘事件 禁止后退键(Backspace)密码或单行.多行文本框除外 function forbidBackSpace(e) { var ev = e || window.event; //获 ...

  10. Handle Refresh Token Using ASP.NET Core 2.0 And JSON Web Token

    来源:   https://www.c-sharpcorner.com/article/handle-refresh-token-using-asp-net-core-2-0-and-json-web ...