題目:洛谷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...