洛谷 P3515 Lightning Conductor —— 决策dp 二分栈 | 分治

题目链接:点我啊╭(╯^╰)╮

题目大意:

在这里插入图片描述

解题思路:

     p i ≥ a j + i − j − a i p_i ≥ a_j + \sqrt{i-j} - a_i piaj+ij ai j ≤ i j≤i ji
     p i ≥ a j + j − i − a i p_i ≥ a_j + \sqrt{j-i} - a_i piaj+ji ai j ≥ i j≥i ji

    只讨论第一个,可转换为:
     p i = m i n ( a j + i − j ) − a i p_i = min(a_j + \sqrt{i-j}) - a_i pi=min(aj+ij )ai
    设 y = a j + i − j y = a_j + \sqrt{i-j} y=aj+ij ,看成是关于 i i i 的曲线,一共有 j j j
    画出图后, i i i 的答案就是在 j ≤ i j≤i ji 的所有曲线里,找最大值
    发现两条曲线最多只有一个交点
    也就是两条曲线,第二条比第一条更优后,第一条就不会再出现在答案里了
    那么就可以求出所有的交点,设 k [ i ] k[i] k[i] q [ i ] q[i] q[i] q [ i + 1 ] q[i+1] q[i+1] 的交点的横坐标
     c a l ( i , j ) cal(i,j) cal(i,j) 表示第 j j j 条曲线,横坐标为 i i i 的值

     c a l ( k [ t a i l − 1 ] , q [ t a i l ] ) < c a l ( k [ t a i l − 1 ] , i ) cal(k[tail-1], q[tail])<cal(k[tail-1], i) cal(k[tail1],q[tail])<cal(k[tail1],i),则 q [ t a i l ] q[tail] q[tail] 这条曲线可以直接去掉了
    注意这里有一个很重要的原因: j j j 较大的曲线增速比 j j j 较小的曲线快!
    具体原因画图即可,这里不作详解
    时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

    注意,上面大概讲述了这题是可以用决策单调来优化的(或者你可以直接用四边形不等式来证)
    发现用代码写出来之后,和普通的二分栈写法几乎一模一样!
    唯一的不同点在于常规写法是二分计算最优决策来决定是否淘汰
    而这道题可以直接代入计算当前队尾是否需要淘汰,这仅仅是这道题的特例!
    把淘汰的那一行换成二分的形式,也是可以直接过的!!(可能要加读入挂或者开O2)


    还有另一种方法
    由经验证明可得这是具有决策单调性的
    且 p [ i ] p[i] p[i] 彼此之间无关
    直接分治解决即可
    时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

二分栈:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
int n, a[maxn], k[maxn], q[maxn];
double p[maxn];

inline double cal(int x, int y){
	return sqrt(x-y) + a[y];
}

int gao(int x, int y){
	int l = y, r = n, mid;
	while(l <= r){
		mid = l + r >> 1;
		if(cal(mid, x) <= cal(mid, y)) r = mid - 1;
		else l = mid + 1;
	}
	return l;
}

void solve(){
	int head = 1, tail = 0;
	for(int i=1; i<=n; i++){
		while(head<tail && cal(k[tail-1], q[tail])<cal(k[tail-1], i)) tail--;
//		while(head<tail && k[tail-1]>=gao(q[tail], i)) tail--;	// 常规写法 
		k[tail] = gao(q[tail], i); q[++tail] = i;
		while(head<tail && k[head]<=i) head++;
		p[i] = max(p[i], cal(i, q[head]));
	}
}

int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) scanf("%d", a+i);
	solve();
	for(int i=1, j=n; i<j; i++, j--) swap(a[i], a[j]), swap(p[i], p[j]);
	solve();
	for(int i=n; i; i--) printf("%d\n", (int)ceil(p[i]) - a[i]);
}

分治:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
int n, a[maxn];
double dp1[maxn], dp2[maxn];

void solve1(int l, int r, int x, int y) {
	if(x > y) return;
	int mid = x + y >> 1, pos;
	for(int i=l; i<=min(r, mid); i++) {
		double tmp = sqrt(double(mid - i)) + a[i];
		if(tmp > dp1[mid]) {
			dp1[mid] = tmp;
			pos = i;
		}
	}
	solve1(l, pos, x, mid-1);
	solve1(pos, r, mid+1, y);
}

void solve2(int l, int r, int x, int y) {
	if(x > y) return;
	int mid = x + y >> 1, pos;
	for(int i=r; i>=max(l, mid); i--) {
		double tmp = sqrt(double(i - mid)) + a[i];
		if(tmp > dp2[mid]) {
			dp2[mid] = tmp;
			pos = i;
		}
	}
	solve2(l, pos, x, mid-1);
	solve2(pos, r, mid+1, y);
}

int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) scanf("%d", a+i);
	solve1(1, n, 1, n);
	solve2(1, n, 1, n);
	for(int i=1; i<=n; i++) printf("%d\n", (int)ceil(max(dp1[i], dp2[i])) - a[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值