向前走莫回头❤

【UOJ 测试】B. 【#245 UER #7】天路(近似算法+RMQ)

 

245. 【UER #7】天路

  隆冬将至,几天后跳蚤国便会迎来寒冬,这对于以血肉之躯和飞机搏斗的跳蚤们来说并不是件好事……然而在悠悠历史岁月中,跳蚤国早已有了应对严寒的应急措施方案!
  在跳蚤国王的带领下,跳蚤们准备启动天路热能塔 —— 红米 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

【题解】【近似算法+RMQ】

【算法一】【线段树或RMQ维护区间最大最小值,暴力求解。期望得分:50分】

【正解来袭!】

【这是一个求解近似值的题,从前貌似没做过。。。】

【其实,我们可以从求准确值开始考虑:那么,即上面50分的做法,枚举长度暴力求解。但我们会发现:近似的是答案而不是区间长度,这样做根本没法近似。所以,我们考虑枚举结果,每次查找当前结果所能满足的最长距离即可。这样我们就可以近似了:枚举时答案的跨越长度是1.05(至于为什么是1.05,我表示我也不清楚。。。hxy说应该是根据允许5%的误差范围选的,好像很有道理!)。】

【查找每次的距离时,我们可以用两个指针t1、t2,如果当前区间[t1,t2]的答案还小于当前的答案,那么,我们将t2后移,否则将t1后移。在判断区间[t1,t2]的答案时,我们用RMQ来查询。】

【RMQ预处理每个区间的最大值和最小值,可以做到O(1)查询】

 

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int maxn[100010][20],minn[100010][20],mi[100010];
int ans[100010],n,a[100010];
inline void pre()
{
	for(int j=1;j<20;++j)
	 for(int i=1;i<=n;++i)
	  if(i+(1<<(j-1))<=n)
	   {
	   	maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
	   	minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
	   }
	for(int i=1;i<=n;++i)
	 {
	 	int k=0;
	 	while((1<<k)<=i) ++k;
	 	mi[i]=k-1;
	 }
}
inline int get_cha(int l,int r)
{
	int ans1=0,ans2=0,k=mi[r-l+1];
	ans1=max(maxn[l][k],maxn[r-(1<<k)+1][k]);
	ans2=min(minn[l][k],minn[r-(1<<k)+1][k]);
	return (ans1-ans2);
}
inline int find(int x)
{
	int t1=1,t2=1,len=0;
	while(t1<=n&&t2<=n)
	 {
	 	int sum=get_cha(t1,t2);
	 	if(sum<=x) 
	 	 {
	 	 	len=max(len,t2-t1+1);
	 	 	if(t2<n) t2++;
	 	 	 else t1++;
		  }
		 else
		  if(t1<n) t1++;
		   else t2++;
	 }
	return len;
}
int main()
{
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;++i)scanf("%d",&a[i]),maxn[i][0]=minn[i][0]=a[i];
	pre();
	memset(ans,127,sizeof(ans));
	int m=get_cha(1,n),len=find(0);
	ans[len]=0;
	for(double i=1;i<=m*1.05;i*=1.05)
	 {
	 	int x=floor(i);
	 	int sum=find(x);
	 	ans[sum]=min(ans[sum],x);
	 }
	for(i=n-1;i>1;--i) ans[i]=min(ans[i],ans[i+1]);
	for(i=2;i<=n;++i) printf("%d\n",ans[i]);
	return 0;
 } 


[附UOJ题解]

 



[反正我没大看懂。。。]

 

 

posted @ 2016-10-18 15:43  lris0-0  阅读(89)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m