NOIP2012提高組 開車旅行

2022-05-19 09:55:31 字數 2732 閱讀 7685

題目:洛谷p1081、vijos p1780、codevs1199。

題目大意:有n座海拔高度不相同的城市(編號1~n),兩城市的距離就是兩城市海拔之差。

規定每次只能從編號小的城市走到編號大的城市。

現在有a和b開車旅行,a每次只開到離當前城市第二近的城市(必須是可以走的,且若兩個城市與該城距離相等,海拔低的最近,下同),b則每次開到最近的城市。

他們輪流開車,a先開,開到另乙個城市換人。規定行駛不超過x的路程。當兩人無法按自己的開法開或將總距離要超過x時旅行結束。

現在有兩小問:

1.給出x,問從**開始旅行,a行的路程除以b行的路程的商最小($\frac=\infty$且$\infty =\infty$,兩個起點的商相等時,取起點海拔較高的)。

2.每次給出起點和x,問a和b各行多少路程。

將有1個第一問,m個第二問。

(以上為題目描述壓縮版.rar)

解題思路:首先,我們得處理出每座城市的最近點和次近點,樸素則$o(n^2)$。(第一部分)

然後,對於開車,我們彷彿找不出除了暴力模擬外的別的方法。

那麼對於兩小問分別要$o(n^2)$和$o(nm)$。(第二部分)

看了看資料規模,70分勉勉強強。

那對於100%的資料,我們的演算法至少是$o(n\log_2 n)$的。

那麼我們對兩部分分別進行優化。

對於第一部分,優化的方法有很多種,例如stl::set($o(n\log_2 n)$)、雙向鍊錶($o(n)$)等。

我不想用set(常數太大),也搞不懂線性的鍊錶,就用了一種帶log的鍊錶。

具體做法如下:先按高度排序,每次找乙個城市,用高度做關鍵字二分出它在鍊錶中的位置,那麼最近點和次近點一定在它前兩個元素到後兩個元素之間,最後刪除當前節點即可(之後不可能再開回該城市)。

對於第二部分,似乎只能模擬,但發現從每個點開始,經過的點是確定的,我們能不能每次跳過一些點,得到結果呢?

用倍增則可以把$o(n)$的查詢優化到$o(\log_2 n)$。

那麼思路就很明顯了。

設f[i][j]表示開$2^j$次車到達的城市(a、b各開一次算一「次」),

fa[i][j]表示開$2^j$次車a開過的路程,

fb[i][j]表示開$2^j$次車b開過的路程。

則我們已經知道f[i][0]、fa[i][0]、fb[i][0],

那麼有f[i][j]=f[f[i][j-1]][j-1],fa[i][j]=fa[i][j-1]+fa[f[i][j-1][j-1],fb[i][j]=fb[i][j-1]+fb[f[i][j-1][j-1]。

然後大家一定知道如何模擬了吧(只要不超,就選j最大的跳)。

最後注意,a最後還可能單獨開一次,不要漏下。

對於第一問,列舉起點讓他開,取最優值即可。

對於第二問,直接查詢,回答詢問。

然後注意細節即可(像我,不注意細節,查了3小時多的錯)。

時間複雜度$o(n\log_2 n)$(鍊錶)+$o(n\log_2 n)$(倍增預處理)+$o(n\log_2 n)$(第一問)+$o(m\log_2 n)$(第二問)=$o((3n+m)\log_2 n)$。

c++ code

(湊合看吧)

#include#include#include#include#define n 100005

#define ll long long

ll n,h[n],f[n][18],fa[n][18],fb[n][18],pp[n];

struct node

}if(p[now].sec&&fa[now][0]<=x)a+=fa[now][0],now=p[now].sec;

}int main();

std::sort(li+1,li+n+1);

li[0]=(node);

li[n+1]=(node);

for(ll i=1;i<=n;++i)li[i].pre=i-1,li[i].nxt=i+1;

memset(fa,-1,sizeof fa);

memset(p,0,sizeof p);

memset(f,0,sizeof f);

memset(pp,0x3f,sizeof pp);

for(ll i=1;i<=n;++i)else

lh=li[li[li[t].pre].pre].h,rh=li[li[li[t].nxt].nxt].h;

if(h[i]-lh>rh-h[i])

}else

}if(fa[i][0]!=-1)fa[i][0]=abs(h[i]-fa[i][0]);

li[li[t].nxt].pre=li[t].pre;

li[li[t].pre].nxt=li[t].nxt;

}for(ll i=1;i<=n;++i)f[i][0]=p[p[i].sec].fst,fb[i][0]=pp[p[i].sec];

for(ll j=1;j<18;++j)

for(ll i=1;i<=n;++i)

if(i+(1if(aa*b>a*ab||aa*b==a*ab&&h[i]>h[ans]&&aa*b&&a*ab)aa=a,ab=b,ans=i;

}printf("%lld\n",ans);

for(ll m=readint();m--;)

return 0;

}

NOIP2012提高組 開車旅行

這題倍增。當拿到a陣列時,我們便記錄他的位置併排個序 再用乙個陣列 然後,我們就將其變成乙個鍊錶的樣子。由於題目要求每次這能從左邊走到右邊,所以我們便從1開始列舉到n,ps luogu的也a了 上標 include include define ll long long define n 10001...

NOIP2012提高組 開車旅行

把ab各走一次算一步 f i j 表示i點跳2 j步走到的點 da i j 表示i點跳2 j步a走的路程 db i j 表示i點跳2 j步b走的路程 倒著掃一遍求從i點a走出一次走到的點toi,記錄da i 0 然後,我們把to i 和i連邊 2.倒著掃一遍求從i b走出一次走到的點t,再列舉所有i...

NOIP2012提高組 開車旅行 題解

題目連線 題目描述 小 a 和小 b 決定利用假期外出旅行,他們將想去的城市從 1 到 n 編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為hi,城市 i 和城市 j 之間的距離 d i,j 恰好是這兩個城市海拔高度之差的絕對值,即d i,j h...