关于二分和单调性的一道好题

发布时间 2023-04-10 16:55:39作者: 铃狐sama

Lightning Conductort题解-二分和单调性的一道好题

题目:Lightning Conductort

网址:

https://www.luogu.com.cn/problem/P3515

题面翻译

给定一个长度为 $n$ 的序列 ${a_n}$,对于每个 $i\in [1,n]$ ,求出一个最小的非负整数 $p$ ,使得 $\forall j\in[1,n]$,都有 $a_j\le a_i+p-\sqrt{|i-j|}$

$1 \le n \le 5\times 10^{5}$,$0 \le a_i \le 10^{9}$ 。

参考的博客

https://www.luogu.com.cn/blog/wby1427421659/lightning-conductort-ti-xie

柿子的推导

先将答案p移到一边并且去掉绝对值

那么可以得到
$$
p>=a[j]-a[i]+\sqrt{i-j}(i>=j)
$$
以及
$$
p>=a[j]-a[i]+\sqrt{j-i}(j>=i)
$$

很显然这两个柿子是十分相似的,那我们试着用前面这个来尝试做一做

我们要试着找最佳的答案(不然呢)

我们可以发现对于每个ans[i](程序是f[i]),是递增的

那么就好打分治了

还不懂?看代码!很详细的(solve1solve2是参考上帖子打出来的,如果分析有误请麻烦指出,十分感谢)

#include<bits/stdc++.h>
using namespace std;
long double ans1[6000005];
long double ans2[6000005];
long long a[6000005];
//十年oi一场梦,不开long long 见祖宗 
//https://www.luogu.com.cn/blog/wby1427421659/lightning-conductort-ti-xie
//我个人只想到了单调性这一步,没想出来怎么做 
//参考博客后就才明白是上课讲的nlogn算法(至少有点像 
//两个solve照着打了一遍,尝试理解,详细见注释 
void solve1(int L,int R,int l,int r){//ans[i]=max(a[j]+sqrt(i-j))-a[i] (1<=j<=i) 
//solve(L,R,l,r)表示f[L]~f[R](下标)的最优决策点在l~r(ans) 
    if (L>R){
    	return;	
	} 
    int mid=(L+R)/2;
    //常规二分 
    int g=0; 
    long double tmp=0;
    ans1[mid]=a[mid];
    for (int i=l;i<=min(r,mid);i++){//利用n的时间找到这一段的最佳决策点 
        tmp=a[i]+sqrt(double(mid-i));//某个点的决策点下的值 
        if (tmp>ans1[mid]){//如果这个点是更佳的点(找最优) 
			ans1[mid]=tmp;//记录这个点的值 
			g=i;//记录这个点的位置 
		}
    }
    if(g==0){//全程都没有找到的话,说明这一部分没有找到最佳 
    	g=mid;//g的位置变为mid,就相当于这一次什么p用都没做,只能继续二分下去		
	}
	ans1[mid]-=a[mid];//这是原式变化而来最后差的部分 
    solve1(L,mid-1,l,g); 
    solve1(mid+1,R,g,r);//最佳决策点是递增的,因此下标增则决策点增,下标减则决策点减 
}
void solve2(int L,int R,int l,int r){//相比solve1就是反着做 
    if (L>R) return ;
    int mid=(L+R)/2;
    int g=0;
    long double tmp=0;
    ans2[mid]=a[mid];
    for (int i=r;i>=max(mid,l);i--){
        tmp=a[i]+sqrt(double(i-mid));
        if (tmp>ans2[mid]){
        	ans2[mid]=tmp;
			g=i;	
		} 
    }
    if (g==0){
    	g=mid; 	
	}
	ans2[mid]-=a[mid];
    solve2(L,mid-1,l,g);
    solve2(mid+1,R,g,r);
}
int main(){
	ios::sync_with_stdio(false);
	long long n;
	cin >> n;
	for(int i=1;i<=n;i++){
		cin >> a[i];
	}
	solve1(1,n,1,n);
	solve2(1,n,1,n);
	for (int i=1;i<=n;i++){
		cout<<(long long)ceil(max(ans1[i],ans2[i]))<<endl;	
	}
    
}