1. 程式人生 > >【NOIP2017提高A組集訓10.22】公交運輸

【NOIP2017提高A組集訓10.22】公交運輸

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; } }