日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【BZOJ - 2144】跳跳棋

發(fā)布時(shí)間:2024/1/18 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【BZOJ - 2144】跳跳棋 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

@跳跳棋@

  • @跳跳棋@
    • @題目描述@
    • @分析1@
    • @分析2@
    • @代碼實(shí)現(xiàn)@
    • @END~@


@題目描述@

Description
跳跳棋是在一條數(shù)軸上進(jìn)行的。棋子只能擺在整點(diǎn)上。每個(gè)點(diǎn)不能擺超過(guò)一個(gè)棋子。我們用跳跳棋來(lái)做一個(gè)簡(jiǎn)單的游戲:棋盤上有3顆棋子,分別在a,b,c這三個(gè)位置。我們要通過(guò)最少的跳動(dòng)把他們的位置移動(dòng)成x,y,z。(棋子是沒(méi)有區(qū)別的)跳動(dòng)的規(guī)則很簡(jiǎn)單,任意選一顆棋子,對(duì)一顆中軸棋子跳動(dòng)。跳動(dòng)后兩顆棋子距離不變。一次只允許跳過(guò)1顆棋子。

寫一個(gè)程序,首先判斷是否可以完成任務(wù)。如果可以,輸出最少需要的跳動(dòng)次數(shù)。

Input
第一行包含三個(gè)整數(shù),表示當(dāng)前棋子的位置a b c。(互不相同)
第二行包含三個(gè)整數(shù),表示目標(biāo)位置x y z。(互不相同)
Output
如果無(wú)解,輸出一行NO。如果可以到達(dá),第一行輸出YES,第二行輸出最少步數(shù)。

Sample Input
1 2 3
0 3 5
Sample Output
YES
2

【范圍】
100% 絕對(duì)值不超過(guò)10^9

@分析1@

這道題真的很跳……

首先這樣的題如果用絕對(duì)坐標(biāo)(a, b, c)來(lái)做絕對(duì)不好做,所以考慮使用相對(duì)距離來(lái)表示點(diǎn)。對(duì)于三元組(a, b, c)將它表示為(l, r, m) = (b-a, c-b, m),含義為:(左邊點(diǎn)離中間點(diǎn)的距離,右邊點(diǎn)離中間點(diǎn)的距離,中間點(diǎn)坐標(biāo))。

然后對(duì)于題目所提到的操作將操作拆為兩種:邊緣點(diǎn)往中間跳,中間點(diǎn)往兩邊跳。
當(dāng)r>l時(shí),左邊點(diǎn)可以往中間跳,從(l, r, m)轉(zhuǎn)移到了(l, r-l, m+l)
當(dāng)l>r時(shí),右邊點(diǎn)可以往中間跳,從(l, r, m)轉(zhuǎn)移到了(l-r, r, m-r)
中間點(diǎn)往兩邊跳沒(méi)有附加條件,從(l, r, m)可以轉(zhuǎn)移到(l, r+l, m-l)與(l+r, r, m+r)
這樣就可以建圖,問(wèn)題轉(zhuǎn)化為求圖上兩點(diǎn)最短距離。

但是……這還是不夠。考慮到中間往兩邊跳是兩邊往中間跳的逆操作,我們可以給邊定向,即只考慮兩邊往中間跳的操作。然后我們發(fā)現(xiàn),對(duì)于某一個(gè)狀態(tài)(l, r, m),通過(guò)兩邊往中間跳的操作后,l和r不會(huì)變得更大,所以這是一個(gè)有向無(wú)環(huán)圖。更進(jìn)一步地,當(dāng)l < r或l > r時(shí),狀態(tài)有唯一轉(zhuǎn)移,否則狀態(tài)無(wú)轉(zhuǎn)移。聯(lián)想到有根樹(shù)上除根節(jié)點(diǎn)外每個(gè)節(jié)點(diǎn)只有一個(gè)父親,根節(jié)點(diǎn)沒(méi)有父親。所以這其實(shí)是一顆樹(shù),且狀態(tài)(l, r-l, m+l)或(l-r, r, m-r)是狀態(tài)(l, r, m)的父節(jié)點(diǎn),當(dāng) l = r 時(shí)狀態(tài)(l, r, m)是根節(jié)點(diǎn)。

問(wèn)題轉(zhuǎn)化為求樹(shù)上兩點(diǎn)最短距離。
再簡(jiǎn)單轉(zhuǎn)化,就是求樹(shù)上兩點(diǎn)的LCA。

@分析2@

分析到這一步已經(jīng)很不容易,但是還還還不夠……
考慮到我們一般做LCA的方法:倍增法。先 O(log2n) O ( l o g 2 n ) 的時(shí)間將兩個(gè)點(diǎn)跳到相同深度,再一級(jí)一級(jí)地同時(shí)往上跳直到兩個(gè)點(diǎn)相同。但是倍增法我們需要 O(n) O ( n ) 的dfs預(yù)處理,所以此處并不適用。

再觀察一下這個(gè)轉(zhuǎn)移的定義:

當(dāng)r>l時(shí),左邊點(diǎn)可以往中間跳,從(l, r, m)轉(zhuǎn)移到了(l, r-l, m+l)
當(dāng)l>r時(shí),右邊點(diǎn)可以往中間跳,從(l, r, m)轉(zhuǎn)移到了(l-r, r, m-r)

再聯(lián)想一下gcd的減法形式gcd(x, y) = gcd(y, x-y) = gcd(x-y, y) ( x > y )

是不是有點(diǎn)像?對(duì)于某一個(gè)狀態(tài),假設(shè) l < r,則(l, r-l, m+l),(l, r-2l, m+2l),…,(l, r%l, m+r/l*l)都是狀態(tài)(l, r, m)的祖先。但是!與gcd算法不同的是,如果r%l=0的話則不能跳到最上面的節(jié)點(diǎn),因?yàn)樵谧詈蟮臅r(shí)候 l 將會(huì)等于 r。

因?yàn)間cd的復(fù)雜度可以證明得到不超過(guò) log(n) l o g ( n ) ,所以我們就有了一個(gè)代替倍增的玩意兒。
具體來(lái)說(shuō),我們先求出兩個(gè)點(diǎn)的深度,以及兩個(gè)點(diǎn)所在樹(shù)的根節(jié)點(diǎn)。比較根節(jié)點(diǎn)判斷是否有解。
【為了簡(jiǎn)稱,我們稱(l, r, m)到達(dá)(l, r%l, m+r/l*l)為“大步跳”】【作者太Lazy了233】
然后,對(duì)于深度較大的節(jié)點(diǎn),假如它通過(guò)“大步跳”到達(dá)的節(jié)點(diǎn)深度比另外一個(gè)節(jié)點(diǎn)大,則它就直接跳到深度相同的地方;否則就跳到“大步跳”到達(dá)的節(jié)點(diǎn),并繼續(xù)迭代。
再然后,取兩個(gè)點(diǎn)“大步跳”到達(dá)的點(diǎn)中深度較大的點(diǎn),假如說(shuō)在這個(gè)點(diǎn)之前它們就已經(jīng)相遇了,就直接跳到相遇的點(diǎn)作為它們兩個(gè)點(diǎn)的LCA;否則就同時(shí)往上跳到深度較大的點(diǎn)的深度,并繼續(xù)迭代。

@代碼實(shí)現(xiàn)@

非常丑陋……不建議參考。
如果還有什么不懂的地方就評(píng)論在下面吧,作者會(huì)盡力解疑的。
【這道題太毒瘤啦……建圖卡死人,LCA卡死人,竟然冒出來(lái)個(gè)gcd……】

#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; inline ll min(ll a, ll b) {return a < b ? a : b; } ll rootl, rootr, rootm; ll dep(ll l, ll r, ll m) {if( l == 0 || r == 0 ) {rootl = l, rootr = r, rootm = m;if( l == 0 ) rootl = r, rootm += r;if( r == 0 ) rootr = l, rootm -= l;return 0;}if( r > l )return dep(l, r%l, m+r/l*l) + r/l;else return dep(l%r, r, m-l/r*r) + l/r; } void sort(ll &a, ll &b, ll &c) {if( a > b ) swap(a, b);if( b > c ) swap(b, c);if( a > b ) swap(a, b); } int main() {ll a, b, c, x, y, z;scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &x, &y, &z);sort(a, b, c), sort(x, y, z);ll l1 = b-a, r1 = c-b, m1 = b;ll l2 = y-x, r2 = z-y, m2 = y;ll d1 = dep(l1, r1, m1);ll rl = rootl, rr = rootr, rm = rootm;ll d2 = dep(l2, r2, m2);if( rootl != rl || rootr != rr || rootm != rm ) {puts("NO");return 0;}else puts("YES");ll ans = 0;while( d1 != d2 ) {if( d1 > d2 ) {if( r1 > l1 ) {ll k = min(r1/l1, d1-d2);d1 -= k, ans += k;l1 = l1, m1 = m1 + k*l1, r1 = r1 - k*l1;}else {ll k = min(l1/r1, d1-d2);d1 -= k, ans += k;r1 = r1, m1 = m1 - k*r1, l1 = l1 - k*r1;}}else {if( r2 > l2 ) {ll k = min(r2/l2, d2-d1);d2 -= k, ans += k;l2 = l2, m2 = m2 + k*l2, r2 = r2 - k*l2;}else {ll k = min(l2/r2, d2-d1);d2 -= k, ans += k;r2 = r2, m2 = m2 - k*r2, l2 = l2 - k*r2;}}}while( l1 != l2 || r1 != r2 || m1 != m2 ) {ll k;if( r1 > l1 ) {if( r2 > l2 ) {if( r1/l1 > r2/l2 )k = r2/l2;else k = r1/l1;d1 -= k, d2 -= k, ans += 2*k;l1 = l1, m1 = m1 + k*l1, r1 = r1 - k*l1;l2 = l2, m2 = m2 + k*l2, r2 = r2 - k*l2;}else {if( r1/l1 > l2/r2 ) {if( l2 % r2 == 0 )k = (m2-m1)/(r2+l1);else k = l2/r2; /* 如果兩個(gè)點(diǎn)能跳到同一節(jié)點(diǎn),則:l1=l2-k*r2,r2=r1-k*l1,m1+k*l1=m2-k*r2 解方程可得k,下面同理 */}else {if( r1 % l1 == 0 )k = (m2-m1)/(r2+l1);else k = r1/l1;}d1 -= k, d2 -= k, ans += 2*k;l1 = l1, m1 = m1 + k*l1, r1 = r1 - k*l1;r2 = r2, m2 = m2 - k*r2, l2 = l2 - k*r2;}}else {if( r2 > l2 ) {if( l1/r1 > r2/l2 ) {if( r2 % l2 == 0 ) k = (m1-m2)/(r1+l2);else k = r2/l2;}else {if( l1 % r1 == 0 )k = (m1-m2)/(r1+l2);else k = l1/r1;}d1 -= k, d2 -= k, ans += 2*k;r1 = r1, m1 = m1 - k*r1, l1 = l1 - k*r1;l2 = l2, m2 = m2 + k*l2, r2 = r2 - k*l2;}else {if( l1/r1 > l2/r2 )k = l2/r2;else k = l1/r1;d1 -= k, d2 -= k, ans += 2*k;r1 = r1, m1 = m1 - k*r1, l1 = l1 - k*r1;r2 = r2, m2 = m2 - k*r2, l2 = l2 - k*r2;}}}printf("%lld\n", ans); }

@END~@

就是這樣,新的一天里,也請(qǐng)多多關(guān)照哦(ノω<。)ノ))☆.。~

總結(jié)

以上是生活随笔為你收集整理的【BZOJ - 2144】跳跳棋的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。