1. 程式人生 > >【BZOJ4709】[Jsoi2011]檸檬 斜率優化+單調棧

【BZOJ4709】[Jsoi2011]檸檬 斜率優化+單調棧

iostream 這樣的 turn times == soft print ras 按順序

【BZOJ4709】[Jsoi2011]檸檬

Description

Flute 很喜歡檸檬。它準備了一串用樹枝串起來的貝殼,打算用一種魔法把貝殼變成檸檬。貝殼一共有 N (1 ≤ N ≤ 100,000) 只,按順序串在樹枝上。為了方便,我們從左到右給貝殼編號 1..N。每只貝殼的大小不一定相同,貝殼 i 的大小為 si(1 ≤ si ≤10,000)。變檸檬的魔法要求,Flute 每次從樹枝一端取下一小段連續的貝殼,並選擇一種貝殼的大小 s0。如果 這一小段貝殼中 大小為 s0 的貝殼有 t 只,那麽魔法可以把這一小段貝殼變成 s0t^2 只檸檬。Flute 可以取任意多次貝殼,直到樹枝上的貝殼被全部取完。各個小段中,Flute 選擇的貝殼大小 s0 可以不同。而最終 Flute 得到的檸檬數,就是所有小段檸檬數的總和。Flute 想知道,它最多能用這一串貝殼變出多少檸檬。請你幫忙解決這個問題。

Input

第 1 行:一個整數,表示 N。 第 2 .. N + 1 行:每行一個整數,第 i + 1 行表示 si。

Output

僅一個整數,表示 Flute 最多能得到的檸檬數。

Sample Input

5
2
2
5
2
3

Sample Output

21
//Flute 先從左端取下 4 只貝殼,它們的大小為 2, 2, 5, 2。選擇 s0 = 2,那麽這一段裏有 3 只大小為 s0 的貝殼,通過魔法可以得到 2×3^2 = 18 只檸檬。再從右端取下最後一只貝殼,通過魔法可以得到 1×3^1 = 3 只檸檬。總共可以得到 18 + 3 = 21 只檸檬。沒有比這更優的方案了。

題解:大爺說他從來沒做過用單調棧優化的斜率優化,唯一的一道還是他自己出的,不過今天我也算是見過第一道這樣的題了。

首先,從兩邊進行操作可以看成只從一邊進行操作,然後我們將原序列反過來再做一遍就行了。

其次,每次施魔法時,區間的左端點和右端點一定都是相同種類的貝殼,這告訴我們應該將每種顏色放到一起處理。然後可以列出DP方程:

$f[i]=max{f[j-1]+(s[i]-s[j]+1)^2*color}$。

其中s[i]表示i這個顏色的前綴和,然後移項

$f[j-1]+(s[j]-1)^2*color=2*s[i]*color*(s[j]-1)+f[i]-s[i]*v[i]$

發現x單調遞增,y單調遞增,k也單調遞增,求的還是上凸包!所以用對於每個顏色都用一個單調棧維護即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define y(_) (f[(_)-1]+(s[_]-1)*(s[_]-1)*j)
#define x(_) (s[_]-1)
#define k (2*s[i]*j)
using namespace std;
const int maxn=100010;
typedef long long ll;
int n,m;
ll ans;
int t[maxn],last[maxn],pre[maxn];
ll v[maxn],s[maxn],f[maxn],g[maxn];
vector<int> st[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j;
	for(i=1;i<=n;i++)	v[i]=rd(),pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1,m=max(m,(int)v[i]);
	for(i=1;i<=m;i++)	t[i]=-1;
	for(i=1;i<=n;i++)
	{
		j=v[i];
		while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]])))	t[j]--,st[j].erase(st[j].end()-1);
		t[j]++,st[j].push_back(i);
		while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1])))	t[j]--,st[j].erase(st[j].end()-1);
		g[i]=f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j;
	}
	for(i=1;(i<<1)<=n;i++)	swap(v[i],v[n-i+1]);
	memset(last,0,sizeof(last));
	for(i=1;i<=n;i++)	pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1;
	for(i=1;i<=m;i++)	st[i].clear(),t[i]=-1;
	for(i=1;i<=n;i++)
	{
		j=v[i];
		while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]])))	t[j]--,st[j].erase(st[j].end()-1);
		t[j]++,st[j].push_back(i);
		while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1])))	t[j]--,st[j].erase(st[j].end()-1);
		f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j;
	}
	ans=0;
	for(i=0;i<=n;i++)	ans=max(ans,g[i]+f[n-i]);
	printf("%lld",ans);
	return 0;
}

【BZOJ4709】[Jsoi2011]檸檬 斜率優化+單調棧