题目描述

$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. Python笔记(二十五)_魔法方法_描述符

    描述符的属性方法 __get__(self, instance, owner): 用于访问属性,返回属性的值 __set__(self, instance, value): 用于给属性赋值时,返回属性 ...

  2. linux下mysql 5.7编写存储过程一直报错说Mysql server version for the right syntax

    首先看下可以正确执行的. 再来看保存时提示出错的 我唯一的区别就是在传参的类型那里有了变化,然而,报错如下 难道是我的类型不支持了吗,最后在一个无意识操作下,直接在类型里面限定长度. 可以运行啦.经过 ...

  3. 关于jdbc和数据库连接池的关系(不是封装的关系)

    你都说是数据库连接池了.那就是连接数据库用的.JDBC是java封装的对数据库的操作.当然你可以自己进一步封装.数据库连接池是JDBC使用的前提,如果连数据库连接池都没连上,JDBC的操作就谈不上了. ...

  4. [13期]mysql-root全手工注入写马实例实战

    回显方便的小工具

  5. IQueryable在LINQ中

    IQueryable接口定义如下: // 摘要: // 提供对未指定数据类型的特定数据源的查询进行计算的功能. public interface IQueryable : IEnumerable { ...

  6. C语言I作业12——学习总结

    1.我学到的内容 二.我的收获 作业 链接 第一次作业 https://www.cnblogs.com/liuxiangjiang/p/11579877.html 第二次作业 https://www. ...

  7. flume 进阶

    一.flume事务 put事务流程: 1.doPut:将批量数据先写入临时缓冲区putList 2.doCommit:检查Channel内存队列是否足够, (1)达到一定时间没有数据写入到putLis ...

  8. [2019杭电多校第七场][hdu6656]Kejin Player

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6656 题意为从i级花费a元有p的概率升到i+1级,有1-p的概率降到x级(x<i),查询从L级升 ...

  9. 大神级回答exists与in的区别

    google搜了一下,很多帖子,而且出发点不同,各有各的道理,但是有一个帖子讲的特别好: http://zhidao.baidu.com/question/134174568.html 忍不住在百度上 ...

  10. 0基础入门 docker 部署 各种 Prometheus 案例 - 程序员学点xx 总集篇

    目录 大家好, 学点xx 系列也推出一段时间了.虽然 yann 能力有限,但还是收到了很多鼓励与赞赏.对这个系列 yann 还是很喜欢的,特别是 Prometheus 篇,在期间经历公众号 100 篇 ...