「Newcoder练习赛40D」小A与最大子段和
生活随笔
收集整理的這篇文章主要介紹了
「Newcoder练习赛40D」小A与最大子段和
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目
挺好的一道題
我們考慮把\(i\)作為選取的最大子段的結束位置,我們如何往前計算貢獻呢
考慮一下這個乘上其在隊列中的位置可以表示為這個數被算了多少次,而我們往前擴展一位當前已經被擴展的就會被計算一次
設\(s_i\)表示序列的前綴和
擴展一次
\[s_i-s_{i-1}\]
再擴展一次
\[s_i-s_{i-1}+s_i-s_{i-2}\]
發現如果我們往前算到第\(j\)項的話貢獻就是
\[(i-j+1)\times s_i-\sum_{k=j-1}^{i-1}s_k\]
如果對前綴和序列在求一個前綴和,得到一個\(S\)序列就變成了這個柿子
\[i\times s_i-s_ij+s_i-S_{i-1}+S_{j-2}\]
發現只有
\[-s_ij+S_{j-2}\]
會影響我們的決策,所以考慮讓這一項最大就好了
設
\[b=-s_ij+S_{j-2}\]
\[s_ij+b=S_{j-2}\]
這不是標準的斜率式嗎,把\((j,S_{j-2})\)看成點,\(s_i\)看成斜率找到一個最優決策點就好了
自然凸殼上二分斜率了
第一次寫這個東西,細節不少
代碼
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define maxn 200005 #define re register #define LL long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) LL s[maxn],pre[maxn]; LL ans=0; int q[maxn],h=1,t,n; inline LL X(int i) {return (LL)i;} inline LL Y(int i) {return pre[((i-2)>0)?(i-2):0];} inline double K(int a,int b) {if(X(a)==X(b)) return 9999999999;return (double)(Y(a)-Y(b))/(double)(X(a)-X(b)); } inline void ins(int x) {while(h<t&&K(q[t-1],q[t])<K(q[t-1],x)) t--;q[++t]=x; } inline int find(LL x) {if(h==t) return q[t];if(h==t-1) {if(K(q[h],q[t])<x) return q[h];return q[t];}int l=h,r=t;while(l<=r) {if(l==r-1) {if(K(q[l],q[r])<x) return q[l];return q[r];}int mid=l+r>>1;if(mid+1>r) break;//細節1if(K(q[mid],q[mid+1])<x) r=mid;//細節2,不能將mid排除,這樣在終止條件的時候就計算不到了else l=mid+1;}return q[t]; } int main() {scanf("%d",&n);for(re int i=1;i<=n;i++) scanf("%lld",&s[i]);for(re int i=1;i<=n;i++) s[i]+=s[i-1];for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+s[i];ins(1);ans=s[1];for(re int i=2;i<=n;i++) {ins(i);int x=find(s[i]);ans=max(ans,(LL)(i+1)*s[i]-s[i]*x-pre[i-1]+pre[((x-2)>0)?(x-2):0]);}printf("%lld\n",ans);return 0; }轉載于:https://www.cnblogs.com/asuldb/p/10392407.html
總結
以上是生活随笔為你收集整理的「Newcoder练习赛40D」小A与最大子段和的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CF div2 PA 2019.02.1
- 下一篇: 西林板材怎么样 评测西林板材的质量和性能