Description

城市中有一条长度为n的道路,每隔1的长度有一个公交车站,编号从0到n,学校在0号车站的位置。其中每个公交车站(除了n号车站)有两个属性ci和vi,代表从这个公交车站出发的公交车的性质。ci代表这个从i出发的公交车,相邻两个停靠站之间的距离。vi表示每坐1站的花费。
注意,一辆公交车出发后会向n号车站的方向行进。同时,一名乘客只能从起点站上车,但可以从任意停靠站下车。校庆志愿者小Z为了帮助校友查询有关城市交通费用的问题,想知道从0号车站(也就是学校)出发,到达每个公交车站的最小花费,于是他找到了你。

Solution

这题暴力十分的好打,但是当你以为可以斜率优化的时候,就会发现有些问题,因为他是一个凸包a+bx样的。
其实你可以观察一下关系,首先肯定要把c[i]和i%c[i]来用10*10的分类,这些都是可以相互转移的,然后当后面有一个v比前面的小,那么我前面的值明显就可以不用要了(这里直接从转移上考虑不是很好,如果在实际当中,你一定会下车换更小的票,也可以发现他的转移不一定要连续,肯定是去过程中v较小的来搞),因为函数的b就是v,所以现在就可以斜率优化了。
我们每次要加进来的这条线,和栈中的倒数第二条的交点,如果在和与最后一条线交点的左边,那么最后一条完全可以被代替掉没用了。
其实这题维护凸包,也可以插入很多条直线到线段树里面去,带个log,常数打的好就行了。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1e6+7;
int i,j,k,l,t,n,m,ans,p;
int c[maxn],v[maxn],mc,oo,dd,x,y;
int f[maxn],o,g[11][11],e[11][11][maxn/2];
int get(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}
int main(){
freopen("bus.in","r",stdin);
freopen("bus.out","w",stdout);
scanf("%d%d",&n,&mc);
fo(i,1,n)c[i]=get(),v[i]=get();
memset(f,127,sizeof(f));oo=f[0];f[1]=0;
fo(i,1,n+1){
if(i==25){
ans=ans;
}
fo(j,1,mc){
k=i%j;if(!g[j][k])continue;
while(g[j][k]>1){
x=e[j][k][g[j][k]],y=e[j][k][g[j][k]-1];
if(f[y]+(i/c[y])*v[y]>=f[x]+(i/c[x])*v[x])break;
g[j][k]--;
}
l=e[j][k][g[j][k]];
f[i]=min(f[i],f[l]+(i/c[l])*v[l]);
}
if(f[i]==5122){
ans=ans;
}
if(i!=1){if(f[i]!=oo)printf("%d ",f[i]);else printf("-1 ");}
if(i==n+1)break;
if(f[i]==oo)continue;
k=i%c[i];f[i]-=(i/c[i])*v[i];
while(g[c[i]][k]&&v[e[c[i]][k][g[c[i]][k]]]>=v[i])g[c[i]][k]--;
while(g[c[i]][k]>1){
x=e[c[i]][k][g[c[i]][k]],y=e[c[i]][k][g[c[i]][k]-1];
if((f[x]-f[i])*(v[x]-v[y])<=(f[y]-f[x])*(v[i]-v[x]))break;
g[c[i]][k]--;
}

e[c[i]][k][++g[c[i]][k]]=i;
}
}