P6087 [JSOI2015]送禮物 01分數規劃+單調佇列+ST表

liuchanglc發表於2020-08-11

P6087 [JSOI2015]送禮物 01分數規劃+單調佇列+ST表

題目背景

\(JYY\)\(CX\) 的結婚紀念日即將到來,\(JYY\) 來到萌萌開的禮品店選購紀念禮物。

萌萌的禮品店很神奇,所有出售的禮物都按照特定的順序都排成一列,而且相鄰 的禮物之間有一種神祕的美感。於是,\(JYY\) 決定從中挑選連續的一些禮物,但究 竟選哪些呢?
題目描述

假設禮品店一共有 \(N\) 件禮物排成一列,每件禮物都有它的美觀度。排在第 \(i\ (1\leqslant i\leqslant N)\) 個位置的禮物美觀度為正整數 \(A_i\)​。\(JYY\) 決定選出其中連續的一段,即編號為 \(i,i+1,\cdots,j-1,j\) 的禮物。選出這些禮物的美觀程度定義為

\[\frac{M(i,j)-m(i,j)}{j-i+K} \]

其中 \(M(i,j)\) 表示 \(\max\{A_i,A_{i+1},\cdots,A_j\}\)\(m(i,j)\) 表示 \(\min\{A_i,A_{i+1},\cdots,A_j\}\)\(K\) 為給定的正整數。 由於不能顯得太小氣,所以 \(JYY\) 所選禮物的件數最少為 \(L\) 件;同時,選得太多也不好拿,因此禮物最多選 \(R\) 件。\(JYY\) 應該如何選擇,才能得到最大的美觀程度?由於禮物實在太多挑花眼,\(JYY\) 打算把這個問題交給會程式設計的你。

輸入格式

本題每個測試點有多組資料。

輸入第一行包含一個正整數 \(T\),表示有 \(T\) 組資料。

每組資料包含兩行。第一行四個非負整數 \(N,K,L,R\)。第二行包含 \(N\) 個正整數,依次表示 \(A_1,A_2,\cdots,A_n\)​。

輸出格式

輸出 \(T\) 行,每行一個非負實數,依次對應每組資料的答案,資料保證答案不 會超過 \(10^3\)。輸出四捨五入保留 \(4\) 位小數。

輸入輸出樣例

輸入

1
5 1 2 4
1 2 3 4 5

輸出

0.7500

說明/提示

對於 \(100\%\) 的資料,\(T\leqslant 10,N,K\leqslant 5\times 10^4\)\(1\leqslant A_i\leqslant 10^8\)\(2\leqslant L,R\leqslant N\)

分析

看到這一個式子,顯然是 \(01\) 分數規劃

但是一般的 \(01\) 分數規劃都是上面一個求和公式比下面一個求和公式

而這一道題則是最大值減去最小值比區間長度加一個定值的形式

我們手玩一下會發現,一個區間的左右兩端一定是該區間的最大值或最小值

因為如果你在最大值或者最小值的基礎上繼續擴充套件的話,分母會變大,結果會變小,肯定不利於我們求解

但是有可能最大值和最小值之間的元素個數小於最小的區間長度 \(l\) ,此時我們就必須向兩邊擴充套件

因此,我們分兩種情況討論:

\(1\) 、 區間的長度大於 \(l\)

此時,我們像正常的 \(01\) 分數規劃一樣二分列舉即可

我們設此時列舉到的價值為 \(mids\)

那麼如果 \(\frac{M(i,j)-m(i,j)}{j-i+K} \geq mids\)

則有 \(\ M(i,j)-m(i,j) \geq mids \times (j-i+K)\)

根據之前推導的結論,兩邊的元素只能是最大值或者最小值

因此我們分類討論

如果區間左邊的元素大於區間右邊的元素

則有 \(a[i]-a[j] \geq mids \times (j-i+K)\)

我們展開移一下項,就有

\(a[i]-a[j] \geq mids \times j- mids \times i +mids \times K\)

\(a[i]+i \times mids - a[j] - j \times mids \geq mids \times K\)

我們令 \(val[i]=a[i]+i \times mids\)

則就有 \(val[i]-val[j] \geq mids \times K\)

其中右邊是一個常數

於是我們驚喜地發現這玩意可以用單調佇列去搞一下

同理,如果區間左邊的元素大於區間右邊的元素

則有 \(a[j]-a[i] \geq mids \times (j-i+K)\)

\(a[j]-a[i] \geq mids \times j- mids \times i +mids \times K\)

\(a[j]- j \times mids - a[i] + i \times mids \geq mids \times K\)

我們令 \(val[i]=a[i]-i \times mids\)

則就有 \(val[j]-val[i] \geq mids \times K\)

也可以用單調佇列去維護

\(2\) 、區間的長度等於 \(l\)

此時我們用 \(ST\) 表預處理出區間最大最小值

每次從左到右掃一邊列舉左端點即可

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const double eqs=1e-6;
int zdz[maxn][20],zxz[maxn][20],a[maxn],n,k,l,r;
double ans=0;
void solve1(){
	int cd=log2(l);
	for(int i=1;i<=n-l+1;i++){
		int j=i+l-1;
		double mmax=max(zdz[i][cd],zdz[j-(1<<cd)+1][cd]);
		double mmin=min(zxz[i][cd],zxz[j-(1<<cd)+1][cd]);
		ans=max(ans,(mmax-mmin)/((double)l-1+(double)k));
	}
}
//列舉區間等於l的情況,直接暴掃
int ql[maxn],qr[maxn],headl,headr,taill,tailr;
double val[maxn];
bool jud(double mids){
	double res=mids*k;
	memset(ql,0,sizeof(ql));
	memset(qr,0,sizeof(qr));
	headl=1,taill=0,headr=1,tailr=0;
	for(int i=1;i<=n;i++){
		val[i]=(double)a[i]-i*mids;
	}
	for(int i=l;i<=n;i++){
		while(headl<=taill && i-ql[headl]+1>r) headl++;
		if(headl<=taill && val[i]-val[ql[headl]]>=res) return 1;
		while(headl<=taill && val[i-l+1]<=val[ql[headl]]) taill--;
		ql[++taill]=i-l+1;
	}
	for(int i=1;i<=n;i++){
		val[i]=(double)a[i]+i*mids;
	}
	for(int i=l;i<=n;i++){
		while(headr<=tailr && i-qr[headr]+1>r) headr++;
		if(headr<=tailr && val[qr[headr]]-val[i]>=res) return 1;
		while(headr<=tailr && val[qr[tailr]]<=val[i-l+1]) tailr--;
		qr[++tailr]=i-l+1;
	}
	return 0;
}
//單調佇列分別搞一下
void solve2(){
	double ml=0,mr=1000,mmids;
	while(mr-ml>eqs){
		mmids=(ml+mr)/2;
		if(jud(mmids)) ml=mmids;
		else mr=mmids;
	}
	ans=max(ans,ml);
}//01分數規劃
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d%d",&n,&k,&l,&r);
		ans=0;
		for(int i=1;i<=n;i++){
			for(int j=0;j<20;j++){
				zxz[i][j]=0x3f3f3f3f,zdz[i][j]=-0x3f3f3f3f;
			}
		}
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			zxz[i][0]=a[i];
			zdz[i][0]=a[i];
		}
		for(int j=1;j<=18;j++){
			for(int i=1;i+(1<<j)-1<=n;i++){
				zxz[i][j]=min(zxz[i][j-1],zxz[i+(1<<(j-1))][j-1]);
				zdz[i][j]=max(zdz[i][j-1],zdz[i+(1<<(j-1))][j-1]);
			}
		}
		//ST表預處理
		solve1();
		//情況一
		solve2();
		//情況二
		printf("%.4lf\n",ans);
	}
	return 0;
}

相關文章