[CSP-S模拟测试]:柱状图(树状数组+二分+三分)
题目描述
$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模拟测试]:柱状图(树状数组+二分+三分)的更多相关文章
- POJ 2182 Lost Cows 【树状数组+二分】
题目链接:http://poj.org/problem?id=2182 Lost Cows Time Limit: 1000MS Memory Limit: 65536K Total Submis ...
- The Stream of Corning 2( 权值线段树/(树状数组+二分) )
题意: 有两种操作:1.在[l,r]上插入一条值为val的线段 2.问p位置上值第k小的线段的值(是否存在) 特别的,询问的时候l和p合起来是一个递增序列 1<=l,r<=1e9:1< ...
- POJ 2828 Buy Tickets (线段树 or 树状数组+二分)
题目链接:http://poj.org/problem?id=2828 题意就是给你n个人,然后每个人按顺序插队,问你最终的顺序是怎么样的. 反过来做就很容易了,从最后一个人开始推,最后一个人位置很容 ...
- TZOJ 4602 高桥和低桥(二分或树状数组+二分)
描述 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”.举 ...
- 树状数组+二分||线段树 HDOJ 5493 Queue
题目传送门 题意:已知每个人的独一无二的身高以及排在他前面或者后面比他高的人数,问身高字典序最小的排法 分析:首先对身高从矮到高排序,那么可以知道每个人有多少人的身高比他高,那么取较小值(k[i], ...
- P2161 [SHOI2009]会场预约[线段树/树状数组+二分/STL]
题目描述 PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个 ...
- 牛客多校第3场 J 思维+树状数组+二分
牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...
- 【CSP模拟赛】奇怪的队列(树状数组 &二分&贪心)
题目描述 nodgd的粉丝太多了,每天都会有很多人排队要签名. 今天有n个人排队,每个人的身高都是一个整数,且互不相同.很不巧,nodgd今天去忙别的事情去了,就只好让这些粉丝们明天再来.同时nod ...
- UVA 10909 Lucky Number(树状数组+二分+YY)
此题测试时预处理等了很久,结果470ms过了...... 题意:开始不怎么懂,结果发现是这个: 波兰裔美国数学家斯塔尼斯拉夫·乌拉姆(Stanislaw Ulam)在20世纪50年代中期开发出了另一种 ...
随机推荐
- 挣值管理(PV、EV、AC、SV、CV、SPI、CPI)
挣值管理法中的PV.EV.AC.SV.CV.SPI.CPI这些英文简写相信把大家都搞得晕头转向的.在挣值管理法中,需要记忆理解的有三个参数:PV.AC.EV. PV:计划值,在即定时间点前计划完成活动 ...
- SpringBoot 使用Mybatis+MySql
pom配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http: ...
- 多线程03-Abort
); t.Abort(); Console.WriteLine(; i < ; i++) { ...
- org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.lang.NumberFormatException: For input string: "W%" ### Cause: java.lang.NumberFormatException: For input s
一个常见的myBatis xml文件中的引号错误: org.apache.ibatis.exceptions.PersistenceException: ### Error querying data ...
- 离线安装 Cloudera ( CDH 5.x )(转载)
要配置生产环境前,最好严格按照官方文档/说明配置环境.比如,官方说这个安装包用于RETHAT6, CENTOS6,那就要装到6的版本下,不然很容易出现各种各样的错. 配置这个CDH5我入了很多坑: C ...
- Java学习day7面向对象编程1-对象和类
一.Java编程中对象和类的概念 1,什么是类? 答:类是客观存在的,抽象的,概念的东西. 2,什么是对象? 答:对象是具体的,实际的,代表一个事物.例如:车是一个类,汽车,自行车就是他的对象. 关于 ...
- BZOJ 1179 (Tarjan缩点+DP)
题面 传送门 分析 由于一个点可以经过多次,显然每个环都会被走一遍. 考虑缩点,将每个强连通分量缩成一个点,点权为联通分量上的所有点之和 缩点后的图是一个有向无环图(DAG) 可拓扑排序,按照拓扑序进 ...
- 7、前端知识点--关于Array.from详解
1.Array.from()方法就是将一个类数组对象 或 可遍历对象 或 可迭代对象 转换成一个真正的数组.浅拷贝的数组实例. 2.那么,什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有len ...
- CentOS安装Python3.x
安装python3.6可能使用的依赖# yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqli ...
- SSH简单概念
Spring:轻量级控制反转(IoC)和面向切面(AOP)的容器框架,让对象与对象之间的关系通过配置文件来管理,减低耦合度 IoC:凡是在容器中配置过的对象才会有Spring提供的服务和功能 AOP: ...