【BZOJ4709】【Jsoi2011】檸檬
阿新 • • 發佈:2018-07-01
source pac 出現的次數 自己 pro 1.0 eps space 貢獻 在相同的值中是第幾個出現的。顯然如果要從別的\(f_j\)轉移到\(f_i\),必須滿足\(a_{j+1}==a_i\)。我們有轉移方程:
\[ f_i=\max \{f_{j-1}+a_i(b_i-b_j+1)^2\}\;\;\;j\le i,a_j=a_i \]
設\(j\)為最優轉移點:
\[ \begin{aligned} f_i&=f_{j-1}+a_i(b_i-(b_j-1))^2\f_i&=f_{j-1}+a_i(b_i^2-2b_i(b_j-1)+(b_j-1)^2)\f_i&=f_{j-1}+a_ib_i^2-2a_ib_i(b_j-1)+a_i(b_j-1)^2\f_{j-1}+a_i(b_j-1)^2&=2a_ib_i(b_j-1)+f_i-a_ib_i^2 \end{aligned} \]
這其實是一個直線的式子:\(k=2a_ib_i\),\(x=(b_j-1)\),\(b=(f_i-a_ib_i^2)\),\(y=f_{j-1}+a_i(b_j-1)^2\).
其中\(a_i\)看似和\(i\)有關,無法繼續推理。但由於轉移的\(j\)滿足\(a_j=a_i\),所以每一個位置的數在參與上述DP時,相關聯的\(a\)其實就是每一個元素自己的數值,是一個定值。
把每一個元素看成二維平面的一個點\((x,y)\)。由於最優轉移相當於最大化截距,那麽最優轉移點\(j\)可以看做在斜率為\(k\)的時候上凸包碰到的第一個點。
那麽我們掃描序列時,維護每一個數值對應的上凸包,每次查詢時在上面二分即可。
時間復雜度\(\mathcal O(n \lg n)\)。
當然,也可以用斜率優化直接做。
Description
傳送門
題意簡述:將序列劃分成任意多段,從每一段選出一個數\(x\),獲得\(在這一段出現的次數x*(x在這一段出現的次數)\)的貢獻。求總貢獻最大值。
Solution
? 首先,要發現一個很重要的性質:如果某一段選了\(x\),那麽這一段一定是以\(x\)開頭、以\(x\)結尾的一段。否則,可以將此段縮減至以\(x\)開頭、以\(x\)結尾的更小的一段,雖然貢獻沒有變,但留給其他段的機會更多。
設\(f_i\)表示\(1...i\)的貢獻最大值。記\(a_i\)表示\(i\)的數值,\(b_i\)表示\(a_i\)
\[ f_i=\max \{f_{j-1}+a_i(b_i-b_j+1)^2\}\;\;\;j\le i,a_j=a_i \]
設\(j\)為最優轉移點:
\[ \begin{aligned} f_i&=f_{j-1}+a_i(b_i-(b_j-1))^2\f_i&=f_{j-1}+a_i(b_i^2-2b_i(b_j-1)+(b_j-1)^2)\f_i&=f_{j-1}+a_ib_i^2-2a_ib_i(b_j-1)+a_i(b_j-1)^2\f_{j-1}+a_i(b_j-1)^2&=2a_ib_i(b_j-1)+f_i-a_ib_i^2 \end{aligned} \]
這其實是一個直線的式子:\(k=2a_ib_i\),\(x=(b_j-1)\),\(b=(f_i-a_ib_i^2)\),\(y=f_{j-1}+a_i(b_j-1)^2\).
其中\(a_i\)看似和\(i\)有關,無法繼續推理。但由於轉移的\(j\)滿足\(a_j=a_i\),所以每一個位置的數在參與上述DP時,相關聯的\(a\)其實就是每一個元素自己的數值,是一個定值。
把每一個元素看成二維平面的一個點\((x,y)\)。由於最優轉移相當於最大化截距,那麽最優轉移點\(j\)可以看做在斜率為\(k\)的時候上凸包碰到的第一個點。
那麽我們掃描序列時,維護每一個數值對應的上凸包,每次查詢時在上面二分即可。
時間復雜度\(\mathcal O(n \lg n)\)。
當然,也可以用斜率優化直接做。
Code
#include <cstdio>
#include <vector>
#define k(i) (2LL*a[i]*b[i])
#define x(i) (b[i]-1LL)
#define y(i) (f[i-1]+1LL*a[i]*(b[i]-1)*(b[i]-1))
#define b(i) (f[i]-1LL*a[i]*b[i]*b[i])
#define pb push_back
#define db pop_back
using namespace std;
typedef long long ll;
const int N=100005,S=10005;
const double EPS=1e-6;
int n,a[N],ecnt[S],b[N];
ll f[N];
vector<int> s[S];
int slen[S];
double slope(int u,int v){return 1.0*(y(v)-y(u))/(x(v)-x(u));}
int query(int col,int k){
k=2*col*k;
int l=0,r=slen[col]-2,mid;
while(l<=r){
mid=(l+r)>>1;
if(slope(s[col][mid],s[col][mid+1])-EPS<=k) r=mid-1;
else l=mid+1;
}
return s[col][l];
}
void insert(int col,int i){
int sz=slen[col];
while(sz>1&&slope(s[col][sz-2],s[col][sz-1])<slope(s[col][sz-1],i))
sz--,slen[col]--,s[col].db();
s[col].pb(i);
slen[col]++;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
b[i]=++ecnt[a[i]];
}
for(int i=1;i<=n;i++){
insert(a[i],i);
int j=query(a[i],b[i]);
f[i]=(j?f[j-1]:f[i-1])+1LL*a[i]*(b[i]-b[j]+1)*(b[i]-b[j]+1);
}
printf("%lld\n",f[n]);
return 0;
}
【BZOJ4709】【Jsoi2011】檸檬