Candy Replenishing Robot
Find the Minimum Number
直接模拟
Melodious password
dfs输出方案
Poles
题意:有多个仓库,只能从后面的仓库运动前面的仓库,现在要把仓库合并成k个,移动一个仓库i到另个仓库j的代价是costi*(xi-xj),问最小代价。
解一下就会发现是个斜率优化,做k次就可以了
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100101 #define LL long long #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) using namespace std; LL sumd[maxn],sumw[maxn],f[2][maxn],d[maxn],w[maxn]; int n,m,p[maxn]; LL calc(int x,int y) { return f[y][x]-sumd[x]; } int main() { scanf("%d %d",&n,&m); dow(i,1,n) scanf("%lld %lld",&d[i],&w[i]); rep(i,1,n) { sumw[i]=sumw[i-1]+w[i]; sumd[i]=sumd[i-1]+d[i]*w[i]; } // rep(i,1,n) printf("%d %lld %lld %lld\n",i,d[i],sumw[i],sumd[i]);printf("\n"); int now=0; rep(i,1,n) f[0][i]=-(sumw[i]-sumw[0])*d[i]+sumd[i]-sumd[0]; // rep(i,1,n) printf("%lld ",f[0][i]);printf("\n"); rep(i,2,m) { now=1-now; rep(j,1,n) f[now][j]=0; int head,tail; head=tail=1; p[1]=i-1; rep(j,i,n) { // printf("%d %d\n",head,tail); // rep(k,head,tail) printf("%d ",p[k]);printf("\n"); while (head<tail && d[j]*(sumw[p[head+1]]-sumw[p[head]])<calc(p[head],1-now)-calc(p[head+1],1-now)) ++head; f[now][j]=(sumw[p[head]]-sumw[j])*d[j]+(sumd[j]-sumd[p[head]])+f[1-now][p[head]]; // printf("%lld %lld\n",calc(p[tail],1-now),calc(j,1-now)); while (head<tail && (calc(p[tail],1-now)-calc(j,1-now))*(sumw[p[tail]]-sumw[p[tail-1]])>(calc(p[tail-1],1-now)-calc(p[tail],1-now))*(sumw[j]-sumw[p[tail]])) --tail; p[++tail]=j; } // rep(j,1,n) printf("%lld ",f[now][j]);printf("\n"); } printf("%lld\n",f[now][n]); return 0; }