【洛谷5665】劃分(決策單調性)
阿新 • • 發佈:2020-11-04
- 給定\(n\)個數\(a_{1\sim n}\),要求找到若干分界點\(k_{1\sim p}\)滿足\(\sum_{i=1}^{k_1}a_i\le\sum_{i=k_1+1}^{k_2}a_i\le\cdots\le\sum_{i=k_p+1}^na_i\)。
- 求\((\sum_{i=1}^{k_1}a_i)^2+(\sum_{i=k_1+1}^{k_2}a_i)^2+...+(\sum_{i=k_p+1}^na_i)^2\)最小值。
- \(n\le4\times10^7\)
決策單調性
感覺這個東西一看就很有單調性。(然而去年的我在考場上並沒能想起來。。。)
不嚴謹地證一下,假設我們現在有三個塊\(A,B,C\)
- \(A,B,C\)獨自成塊:\(A^2+B^2+C^2\)。
- \(A\)和\(B\)合併:\(A^2+B^2+C^2+2AB\)。
- \(B\)和\(C\)合併:\(A^2+B^2+C^2+2AC\)。
顯然這三種可能性代價依次遞增。
也就是說,我們的決策點能右移一定右移。
單調佇列
\(i\)能從\(j\)轉移的條件是\(s_i-s_j\ge s_j-s_{g_j}\),其中\(g_j\)為\(j\)的決策點。
移下項令其分為只與\(i\)有關和只與\(j\)有關兩部分,就是\(s_i\ge 2s_j-s_{g_j}\)。
那麼我們只要開一個單調佇列,維護\(2s_j-s_{g_j}\)
高精?
又不是真的在考場上。
懶了點直接__in128
了。
程式碼:\(O(n)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 40000000 using namespace std; int n,ty,a[N+5],q[N+5],g[N+5];long long s[N+5]; namespace Data//大資料生成 { #define M 100000 int x,y,z,m,b[N+5];I void Work() { RI i,j,p,l,r;scanf("%d%d%d%d%d%d",&x,&y,&z,b+1,b+2,&m); for(i=3;i<=n;++i) b[i]=(1LL*x*b[i-1]+1LL*y*b[i-2]+z)&((1<<30)-1); for(i=j=1;i<=m;++i) {scanf("%d%d%d",&p,&l,&r);W(j<=p) a[j]=(b[j]%(r-l+1))+l,++j;}//甚至造資料都要寫個雙指標。。。 } } I void write(__int128 x) {putchar(x?(x>9&&(write(x/10),0),x%10+48):48);}//__int128需要輸優 int main() { RI i;if(scanf("%d%d",&n,&ty),!ty) for(i=1;i<=n;++i) scanf("%lld",a+i);else Data::Work();//讀入資料 RI H=1,T=1;for(i=1;i<=n;++i) s[i]=s[i-1]+a[i];for(q[1]=0,i=1;i<=n;++i) { #define Calc(j) (2*s[j]-s[g[j]])//從j轉移 W(H^T&&Calc(q[H+1])<=s[i]) ++H;g[i]=q[H];W(H^T&&Calc(q[T])>=Calc(i)) --T;q[++T]=i;//每次取隊首作為決策點,維護Calc()單調遞增 } __int128 t=0;for(i=n;i;i=g[i]) t+=(__int128)(s[i]-s[g[i]])*(s[i]-s[g[i]]);return write(t),0;//計算答案 }