【NOIP2017提高A組集訓10.22】公交運輸
阿新 • • 發佈:2019-02-07
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;
}
}