1. 程式人生 > 實用技巧 >[SDOI2012]任務安排

[SDOI2012]任務安排

解析

預算費用,和Luogu P2365 任務安排一樣
得到了同樣的式子
但是我們發現 \(sumt_i\) 可能小於零
也就是說我們需要的斜率可能不再具有單調性
所以我們要維護整個凸殼
那麼怎麼找到最佳的決策點呢?
其實就是 \(slope(m,m-1) < sumt_i\)\(slope(m,m+1) >= sumt_i\) 中的 \(m\)
於是我們可以二分整個凸殼,找到這樣的點
也就是判斷等價於 \(slope(m , m + 1) >= sumt_i\)
滿足這個不等式的決策可能成為最有決策,記錄下來
然後我們要找凸殼中更靠前的,於是 \(r=mid-1\)

\(Code\)

#include<cstdio>
using namespace std;
typedef long long LL;

const int N = 3e5 + 5;
int n , q[N];
LL f[N] , T[N] , C[N] , s , t , c;

double slope(int u , int v)
{
	if (C[u] == C[v]) return 1.0 / 0.0; //當除數為零時,我們就返回個極大值,不寫這句只有80pts
	return 1.0 * ((f[u] - s * C[u]) - (f[v] - s * C[v])) / (1.0 * C[u] - C[v]);
}

int  search(int l , int r , int m)
{
	if (l == r) return q[l];
	int mid , res = r;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		if (slope(q[mid] , q[mid + 1]) >= m) res = mid , r = mid - 1;
		else l = mid + 1;
	}
	return q[res];
}

int main()
{
	scanf("%d%lld" , &n , &s);
	for(register int i = 1; i <= n; i++)  
		scanf("%lld%lld" , &t , &c) , T[i] = T[i - 1] + t , C[i] = C[i - 1] + c;
	int r;
	q[r = 1] = 0;
	for(register int i = 1; i <= n; i++)
	{
		int j = search(1 , r , T[i]);
		f[i] = f[j] + T[i] * (C[i] - C[j]) + s * (C[n] - C[j]);
		while (r > 1 && slope(i , q[r]) < slope(q[r] , q[r - 1])) --r;
		q[++r] = i;
	}
	printf("%lld" , f[n]);
}