bzoj4518
征途
Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。
第一行两个数 n、m。
第二行 n 个数,表示 n 段路的长度
一个数,最小方差乘以 m^2 后的值
5 2
1 2 5 8 6
Sample Output
36
Hint
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
sol:写的我脑袋好疼啊qaq
对于斜率优化dp的题永远只会先写暴力qaq
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=3005; int n,m; ll a[N],Qzh[N]; ll dp[N][N]; inline ll Sqr(ll x) { return x*x; } /* 原式:Ans=(S1-Sum/m)^2+(S2-Sum/m)^2+...+(Sm-Sum/m)^2)/m*m*m ---> Ans=m*(S1-Sum/m)^2+(S2-Sum/m)^2+...+(Sm-Sum/m)^2) ---> Ans=m*((S1^2+S2^2+...+Sm^2)-2*(S1+S2+...Sm)*Sum/m+m*Sum*Sum/m/m) ---> Ans=m*(S1^2+S2^2+...Sm^2)-2*Sum*Sum+m*Sum*Sum/m ---> Ans=m*(S1^2+S2^2+...Sm^2)-Sum*Sum 无视掉后面的常数 Sum*Sum 可以得到当S1^2+S2^2+...+Sn^2最小的时候是最优的 */ int main() { int i,j,k; R(n); R(m); for(i=1;i<=n;i++) R(a[i]); for(i=1;i<=n;i++) Qzh[i]=Qzh[i-1]+a[i]; memset(dp,63,sizeof dp); dp[0][0]=0; for(i=1;i<=n;i++) { for(j=1;j<=min(m,i);j++) { for(k=j-1;k<=i-1;k++) { dp[i][j]=min(dp[i][j],dp[k][j-1]+Sqr(Qzh[i]-Qzh[k])); } } } // printf("dp[%d][%d]=%lld\n",n,m,dp[n][m]); Wl(dp[n][m]*m-Sqr(Qzh[n])); return 0; } /* input 5 2 1 2 5 8 6 output 36 */
然后我偷懒了,不写过程了,所有过程都在代码上面有(其实是因为找不到草稿纸滑稽)
/* 原式:Ans=(S1-Sum/m)^2+(S2-Sum/m)^2+...+(Sm-Sum/m)^2)/m*m*m ---> Ans=m*(S1-Sum/m)^2+(S2-Sum/m)^2+...+(Sm-Sum/m)^2) ---> Ans=m*((S1^2+S2^2+...+Sm^2)-2*(S1+S2+...Sm)*Sum/m+m*Sum*Sum/m/m) ---> Ans=m*(S1^2+S2^2+...Sm^2)-2*Sum*Sum+m*Sum*Sum/m ---> Ans=m*(S1^2+S2^2+...Sm^2)-Sum*Sum 无视掉后面的常数 Sum*Sum 可以得到当S1^2+S2^2+...+Sn^2最小的时候是最优的 然后就是斜率优化dp板子了吧qaq dp[i][j]表示到第i位,走了j天的最小平方和 i,j,k (i<j<k) 若转移dp[k][o]时i比j优 原式dp[i][o-1]+(Qzh[k]-Qzh[i])*(Qzh[k]-Qzh[i]) <= dp[j][o-1]+(Qzh[k]-Qzh[j])*(Qzh[k]-Qzh[j]) --> dp[i][o-1]+Qzh[k]*Qzh[k]-2*Qzh[k]*Qzh[i]+Qzh[i]*Qzh[i] <= dp[j][o-1]+Qzh[k]*Qzh[k]-2*Qzh[k]*Qzh[j]+Qzh[j]*Qzh[j] --> dp[i][o-1]-dp[j][o-1]-2*Qzh[k]*(Qzh[i]-Qzh[j]) <= Qzh[j]*Qzh[j]-Qzh[i]*Qzh[i] 见1),2),3) 可能只有3)是有用的 1) --> dp[i][o-1]-dp[j][o-1]+Qzh[i]*Qzh[i]-Qzh[j]*Qzh[j] <= 2*(Qzh[i]-Qzh[j])*Qzh[k] --> (dp[i][o-1]-dp[j][o-1]+Qzh[i]*Qzh[i]-Qzh[j]*Qzh[j])/(2*(Qzh[i]-Qzh[j])) >= Qzh[k] (注意Qzh[i]<Qzh[j],除过去会变号) 2) --> dp[i][o-1]-dp[j][o-1] <= (Qzh[j]+Qzh[i])*(Qzh[j]-Qzh[i])-2*Qzh[k]*(Qzh[j]-Qzh[i]) --> dp[i][o-1]-dp[j][o-1] <= (Qzh[j]+Qzh[i]-2*Qzh[k])*(Qzh[j]-Qzh[i]) 即当 dp[i][o-1]-dp[j][o-1] <= (Qzh[j]+Qzh[i]-2*Qzh[k])*(Qzh[j]-Qzh[i]) i比j优,否则j比i优 3) --> dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j] <= 2*Qzh[k]*(Qzh[i]-Qzh[j]) 所以i<j<k且满足上式时i比j优 用G[i][j]表示i-j这段的斜率就是(dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j])/2*Qzh[k]*(Qzh[i]-Qzh[j]) 当G[i][j]<=1时i就比j优了 同理当G[i][j]>=G[j][k]时j就没用了 Ps:以上全部在i<j<k的意义下 */ #include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=3005; int n,m; ll a[N],Qzh[N]; ll dp[N][N]; int Queue[N]; //满足dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j] <= Qzh[k]*2*(Qzh[i]-Qzh[j])则i比j优 inline bool Pand(int i,int j,int k,int o) //i<j<k且i比j优 { ll S1=dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j]; ll S2=Qzh[k]*2*(Qzh[i]-Qzh[j]); return (S1<=S2)?1:0; } //G[i][j]=(dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j])/2*Qzh[ooo]*(Qzh[i]-Qzh[j]) (其中Qzh[ooo]可消去) inline bool InvPand(int i,int j,int k,int o) //i<j<k且i比j优 { //G[i][j] ll S1=(dp[i][o-1]+Qzh[i]*Qzh[i]-dp[j][o-1]-Qzh[j]*Qzh[j])*(Qzh[j]-Qzh[k]); ll S2=(dp[j][o-1]+Qzh[j]*Qzh[j]-dp[k][o-1]-Qzh[k]*Qzh[k])*(Qzh[i]-Qzh[j]); return (S1>=S2)?1:0; } int main() { int i,j,k; R(n); R(m); for(i=1;i<=n;i++) R(a[i]); for(i=1;i<=n;i++) Qzh[i]=Qzh[i-1]+a[i]; for(i=1;i<=n;i++) dp[i][1]=Qzh[i]*Qzh[i]; for(i=2;i<=m;i++) { int Head=0,Tail=0; Queue[0]=0; for(j=1;j<=n;j++) { while(Head<Tail&&(!Pand(Queue[Head],Queue[Head+1],j,i))) Head++; dp[j][i]=dp[Queue[Head]][i-1]+(Qzh[j]-Qzh[Queue[Head]])*(Qzh[j]-Qzh[Queue[Head]]); while(Head<Tail&&InvPand(Queue[Tail-1],Queue[Tail],j,i)) Tail--; Queue[++Tail]=j; } } Wl(dp[n][m]*m-Qzh[n]*Qzh[n]); return 0; } /* input 5 2 1 2 5 8 6 output 36 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!