【JZOJ4461】【GDOI2016模拟4.21】灯塔 分治

题面

GDOI是一个地处丘陵的小国,为了边防建设,国王希望在国界线上的某一座山峰上建立一座灯塔,照亮整个边界。而灯塔建设的调研工作,就交给了你。
GDOI的国境线上有N座连续的山峰,其中第i座的高度是hi。为了简单起见,我们认为这N座山峰排成了连续一条直线。
如果在第i座山峰上建立一座高度为p(p>=0)的灯塔,你会发现,这座灯塔能够照亮第j座山峰,当且仅当满足如下不等式:
hj <= hi + p - sqrt(|i - j|)

GDOI国王希望对于每一座山峰,你都能提供建造一座能够照亮全部其他山峰的灯塔所需要的最小高度。你能做到吗?
对于100%的数据满足(1<N<=10^5,0<h_i<=10^9)

ST表做法

注意到(sqrt(|i - j|))的取值最多(sqrt n)种,我们对于每种取值,找出距离范围。
然后在这个范围内RMQ即可,可以用ST表
时间复杂度为(O(nsqrt n))ST表的复杂度是(O(1))的。

分治做法

对于一个点,我们分开考虑左右两边的贡献。
在这里只讨论左边的贡献,右边则类似。
不难发现,本题具有决策单调性,具体来说:
对于一个点(i),设它的决策点是(j),那么任意(i'>i),都有决策点(j'>j)
于是我们就可以分治解决。
对于([l,r])区间,先算出(mid)的决策点(p_{mid}),那么左区间的决策点不超过(p_{mid}),右区间的决策点不小于(p_{mid})
时间复杂度为(O(nlog_n))

Code

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const int inf=0x7fffffff;
const int maxn=100006;
int n,h[maxn];
double ans[maxn],Ans[maxn];
double dis(int v,int u){return h[u]-h[v]+sqrt(abs(u-v));}
void dfs(int l,int r,int L,int R){
	int mid=(l+r)/2,tmp=0;
	double tmd=-inf;
	if (l>r) return;
	fo(i,max(L,mid),R){
		double tmb=dis(mid,i);
		if (tmp==0 || tmb>tmd){
			tmp=i;
			tmd=tmb;
		}
	}
	ans[mid]=tmd;
	dfs(mid+1,r,tmp,R);
	dfs(l,mid-1,L,tmp);
}
void Dfs(int l,int r,int L,int R){
	int mid=(l+r)/2,tmp=0;
	double tmd=-inf;
	if (l>r) return;
	fo(i,L,min(mid,R)){
		double tmb=dis(mid,i);
		if (tmp==0 || tmb>tmd){
			tmp=i;
			tmd=tmb;
		}
	}
	Ans[mid]=tmd;
	Dfs(mid+1,r,tmp,R);
	Dfs(l,mid-1,L,tmp);
}
int main(){
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	scanf("%d",&n);
	memset(ans,127,sizeof ans);
	memset(Ans,127,sizeof Ans);
	fo(i,1,n) scanf("%d",&h[i]);
	dfs(1,n,1,n);
	Dfs(1,n,1,n);
	fo(i,1,n){
		ll cans=max(0ll,(ll)max(ceil(Ans[i]),ceil(ans[i])));
		printf("%lld
",cans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/hiweibolu/p/6730583.html