[BZOJ4709][JSOI2011]檸檬 決策單調性優化dp


 題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709

我好弱啊QAQ,網上dalao們的題解根本看不懂啊,折騰了幾個小時,有一點明白了。

首先要把朴素dp方程退出來。

①題目中說每次從序列的左右選一端取,但是如果你真的照着題目說的這樣做我也不知道會怎么樣。事實上很明顯不管怎么取,最終答案都只跟划分出的是哪幾個區間有關。所以不妨從左端開始取。

②如果取一個區間,區間第一個貝殼的大小和最后一個貝殼的大小不一樣,那么很明顯可以去掉第一個或最后一個貝殼,把他們加入另一個區間貢獻答案,而這一次選取的區間本身答案不會變。於是我們每次取一段區間都可以貪心地來取,使得第一個貝殼和最后一個貝殼大小一定相同。

有了這兩個准則方程很容易就出來了$$f[i]=max\{f[j-1]+a[i]*(s[i]-s[j]+1)^2\}$$

其中$s[i]$表示直到第$i$個數$a[i]$出現的次數。

考慮這個式子中的單調性,可以發現$s[i]$是遞增的也就是說$(s[i]-s[j]+1)^2$會增大,而且會增大地越來越快。這就說明如果之前有一個$k<j$滿足$k$更優,則$k$會永遠比$j$更優。

於是對於每一個$a[i]$可以用一個單調棧維護,當棧頂第二個元素比第一個元素更優時,彈出就行了,直到結束時,取棧頂元素作為決策。(不彈出)

但是這樣還有一個問題,可以發現,在某些情況下,可能會出現第二個元素劣於第一個元素,但是卻第三個元素優於第一個元素,怎么辦呢?

對於任意的$j1<j2<i1<i2$,想一想可以發現如果$j1$超過$i1$的時間小於$j2$超過$i1$的時間,那么$j1$超過$i2$的時間也一定比$j2$超過$i2$的時間早。對於求某一個$j$超過$k$的時間,可以用二分來求。這個時候方法就出來了,在將$i$壓入棧之前,我們先判斷第二個元素超過$i$的時間是否小於第一個元素超過$i$的時間,如果是就彈棧,直到不滿足條件,將$i$壓入棧中。

這樣做就滿足了每一個元素超過上一個元素的時間也是單調的。

 

 1 /**************************************************************
 2     Problem: 4709
 3     User: C20161009
 4     Language: C++
 5     Result: Accepted
 6     Time:444 ms
 7     Memory:3024 kb
 8 ****************************************************************/
 9  
10 #include<cstdio>
11 #include<cstring>
12 #include<algorithm>
13 #include<vector>
14 using namespace std;
15 typedef long long ll;
16 int inline readint(){
17     int Num;char ch;
18     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
19     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
20     return Num;
21 }
22 ll f[100010];
23 int n,a[100010];
24 int cnt[100010],s[100010];
25 vector <int> sta[10010];
26 ll inline cal(int x,int y){
27     return f[x-1]+(ll)a[x]*y*y;
28 }
29 int beyond(int x,int y){
30     int l=1,r=n,ret=n+1;
31     while(l<=r){
32         int mid=l+r>>1;
33         if(cal(x,mid-s[x]+1)>=cal(y,mid-s[y]+1)){
34             ret=mid;
35             r=mid-1;
36         }
37         else l=mid+1; 
38     }
39     return ret;
40 }
41 int main(){
42     n=readint();
43     for(int i=1;i<=n;i++){
44         int x=readint();
45         a[i]=x;
46         s[i]=++cnt[x];
47         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=beyond(sta[x][sta[x].size()-1],i)) sta[x].pop_back();
48         sta[x].push_back(i);
49         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=s[i]) sta[x].pop_back();
50         f[i]=cal(sta[x][sta[x].size()-1],s[i]-s[sta[x][sta[x].size()-1]]+1);
51     }
52     printf("%lld\n",f[n]);
53     return 0;
54 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM