在跳蚤国王的带领下,跳蚤们准备启动天路热能塔 —— 红米 note7(红米 note7 为发烧而生)。这座热能塔高耸入云,直接穿出大气层从太空中直接吸收太阳光,垂直向下将热能送往跳蚤国各个角落。热能塔的制造工艺巧夺天工,被誉为“带来温暖的天路”。
但是跳晚们为了让跳蚤们都因为天气寒冷赖在被子里不肯起床,在热能塔启动后一定会歇斯底里地进攻。跳蚤国高级间谍的情报显示,跳晚国计划将发射 n台三星 note7 向热能塔发起进攻。进攻将会按一定顺序进行,其中第 ii 次进攻的高度为 ai(1≤i≤n)。
为了防止热能塔被炸毁,跳蚤国王特地派尛焱轟(一种新型交通工具,运载能力是小火车的三次幂)运送来了跳蚤们刚研制出不久的新型材料 —— Nokia1050。跳蚤们将会把 Nokia1050 装在热能塔上的某一连续的高度区间上以抵挡进攻。
现在,跳蚤国王想在热能塔受损程度和材料消耗量之间进行取舍。所以对于每个 2≤k≤n,跳蚤国王想知道整个攻击过程中如果想让 Nokia1050 在某一时段至少挡住连续 k次攻击,那么安装 Nokia1050 的高度区间的长度至少是多少。其中,若高度区间为 [l,r],则长度为 r−l。
事实上,间谍的消息也不见得会多么靠谱,所以跳蚤国王仅想知道一个不那么准确的答案。具体来说:
如果对于每个 k你输出的答案 ck与标准答案 c^k 的相对误差均不超过 5%,则算作正确。即:
∣ck−c^k∣≤5%⋅c^k
输入格式
第一行一个正整数 n,保证 n≥2。
第二行 n个正整数 a1,…,an,按顺序给出每次进攻时三星 note7 的高度。
输出格式
输出 n−1行,其中第 k−1行表示至少抵挡连续 k次攻击时所需的最短高度区间长度。(2≤k≤n)
因为十分重要所以说两遍,如果对于每个 kk 你输出的答案 ck与标准答案 c^k的相对误差均不超过 5%,则算作正确。即:∣ck−c^k∣≤5%⋅c^k
样例一
input
4
1 7 5 2
output
2
5
6
explanation
当 k=2时,最优高度区间为 [5,7];
当 k=3时,最优高度区间为 [2,7];
当 k=4 时,最优高度区间为 [1,7];
注意 k=2时不能选择高度区间 [1,2],虽然能够拦截下第 1次和第 4次攻击,但这两次攻击并不连续。
样例二
input
10
26 723 970 13 422 968 875 329 234 983
output
93
546
639
734
749
957
957
957
970
explanation
样例输出给出的为准确答案,注意下面的输出也是可接受的:
93
540
630
730
740
960
960
960
970
样例三
见样例数据下载。
限制与约定
对于所有数据,保证 1≤ai≤106。
时间限制:1s
空间限制:256MB
题解:
算法一:O(n^2logn)
暴力枚举区间,然后用线段树维护区间最值,O(logn)查询。也可以用rmq,O(1)查询。
算法二:
对于 6,7 号测试点,n<=10^5。a[i]数据随机
随机数据的前缀最小值只会变化 O(logn)次
所以当我们固定左端点移动右端点时,区间内最大值与最小值至多只会变化 O(logn) 次。
拿个单调栈维护一下……每次给对应的长度区间更新答案。
算法三:
还是数据随机的情况,观察数据我们注意到如果区间长度够长,那么答案就非常接近10^6。
所以我们设一个几百左右的值 l,然后处理小于 l 时的答案,剩下来的输出 10^6 即可。
同样可以获得 70 分。
算法四:
你想,要是直接把权值除以一个大常数,做完之后再乘回来,看上去好像很近似!但是冷静冷静就会发现这样做出来是个绝对误差的,如果碰到233333−233332=1,除去一个大常数很显然是0,那么你再乘回去也没用。
一个思路是,我们应该是枚举一个 k ,然后某种方式维护一下,但这个看上去就很没办法改造为近似算法。
那如果告诉你一个常数 c,我们来把答案 ck 划一划范围,那 ck≥c 可做吗?
因为最终的答案肯定是单调递增的,所以范围肯定是一段连续的 k。我们可以两个指针扫一下,对于每个ck我们求解出最长的满足当前答案的长度x,并用ck更新长度ans[x]的答案。不是每个长度都有可能被计算到,所以最后我们需要ans[x]=max(ans[x],ans[x+1])更长的都能满足,那么当前的必然能满足。(如何求满足条件的最长长度,设两个指针i,j,如果getmx(i,j)-getmn(i,j)在要求的范围之内,就不断的后移j.然后更新答案,这时候j可能不能再移了,所以左指针i右移,但是这样会跳过很多的区间,其实没关系,因为我们要求的是最长的长度,所以如果j不动,i后移那么答案一定不会大于上次的答案。所以我们可以O(n)求解)
于是我们就可以每次找找 c,划分 k 的范围。当范围足够小的时候,就可以把这一段的 kk 都用同一个数近似了。
事实上,你只需要取:
1.05^0,1.05^1,1.05^2,1.05^3,…
每一段里面随便找个数当答案一定不会超过相对误差的容许范围。
O(nlog1+ϵV)=O(nlogV/log(1+ϵ))=O(ϵ−1nlogV).
这个算法其实说明,所有 f(i)随 i递增且有个算法告诉我 f(i)≥k的第一个 i在哪里的话,都可以套这个近似算法!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<ctime>
#define N 200003
using namespace std;
int n,m;
int tr[N*4],tr1[N*4],a[N];
int l[N],f[30][N],f1[30][N];
double ans[N],x;
struct data
{
int maxn,minn;
};
void rmq()
{
for (int i=1;i<=n;i++) f[0][i]=a[i];
for (int i=1;i<=19;i++)
for (int j=1;j<=n;j++)
if (j+(1<<i)-1<=n)
f[i][j]=max(f[i-1][j],f[i-1][j+(1<<(i-1))]);
for (int i=1;i<=n;i++) f1[0][i]=a[i];
for (int i=1;i<=19;i++)
for (int j=1;j<=n;j++)
if (j+(1<<i)-1<=n)
f1[i][j]=min(f1[i-1][j],f1[i-1][j+(1<<(i-1))]);
int j=0;
for (int i=1;i<=n;i++)
{
if ((1<<(j+1))<=i) j++;
l[i]=j;
}
}
int getmx(int x,int y)
{
int t=l[y-x];
return max(f[t][x],f[t][y-(1<<t)+1]);
}
int getmn(int x,int y)
{
int t=l[y-x];
return min(f1[t][x],f1[t][y-(1<<t)+1]);
}
int getl(int x)
{
int i,j,ans;
i=j=1; ans=0;
while (i<=n)
{
while (j<=n&&getmx(i,j)-getmn(i,j)<=x) j++;
ans=max(ans,j-i);
i++;
}
return ans;
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
rmq();
memset(ans,127,sizeof(ans));
int i=getl(0);
ans[i]=min(ans[i],x);
for (x=1.05;x<=1050000;x*=1.05)
{
int i=getl((int)x);
ans[i]=min(ans[i],x);
}
for (int i=n-1;i>=1;i--)
ans[i]=min(ans[i],ans[i+1]);
for (int i=2;i<=n;i++)
printf("%d\n",(int)ans[i]);
}