2018瀋陽現場賽K題題解 約瑟夫問題

2021-09-25 02:14:53 字數 3878 閱讀 2678

題目大意:給定初始人數n,步長m,求第k個被彈出的人的編號。

這題有乙個非常重要的條件,那就是sum(min(m, k)) <= 2e6

因此我們可以分情況討論。下面假設編號從0開始。

k < m

此時k最大為2e6。因此我們可以使用乙個o(k)的方法求出答案。

設f(n, k)為初始為n個人時第k個被彈出的人。第乙個被彈出的人編號顯然為(m-1) % n,從下乙個人開始從0開始重新編號,問題變成了求f(n-1, k-1),假設其已知,那麼再把結果的座標轉換回來即可。表示式為f(n, k) = (f(n-1, k-1) + m) % n。由於k < n,因此用它求解複雜度為o(k)。

m <= k

此時m最大為2e6,因此我們可以使用乙個o(m)的方法求出答案。

看了大佬的部落格之後豁然開朗:

maskray.me/blog/2013-08-27-josephus-problem-two-log-n-solutions

這裡我用自己的語言複述一下。

設第p次報數的人為y,假設這次報數y沒有被彈出,那麼p % m < m-1,設下次y報數是第q次報數。顯然第0到p-1次報數總共有a = floor(p / m)個人被彈出,所以第p次報數之後總共有n - a個人,所以q = p + n - a。第k個被彈出去的人被彈出時顯然是第(k-1) * m + m-1次報數。如果我們能用q來推出p,那麼就可以一直往前推,直到p < n,這就是這個人的編號。

整理一下,目前已知條件只有三個:

p % m < m-1

q = p + n - a

a = floor(p / m)

我們先嘗試引入模數來消掉向下取整。設p = a * m + b

則已知條件轉化為:

p = a * m + b (1)

b < m-1 (2)

q = p + n - a (3)

把(1)代入(3),得q = (m-1)*a + n + b

即 q - n = (m-1) * a + b, b < m-1

顯然就有a = floor((q - n) / (m - 1))

這樣我們就用q表示了a。

將其代入(3)中,得

p = q - n + floor((q - n) / (m - 1))

通過之前提到的策略,從(k-1) * m + m-1向前推便可以推出答案。

式子第二項在遞推過程中顯然是指數減小的,因此不影響複雜度。所以推到答案大約需要

((k-1)*m + m-1) / n

次迭代。由於k < n,所以複雜度為o(m) //(我不知道為什麼上面的鏈結裡的文章裡說複雜度是o(log n))

但是存在乙個問題,那就是這題需要初始化為(k-1) * m + m-1,會爆long long,要用__int128,但是這題除了gym外目前只找到hdu能交:

acm.hdu.edu.cn/showproblem.php?pid=6458

但是gym和hdu都是windows評測機,不能用__int128,而手寫的_uint128 tle了,因此這題我沒有用這個方法過掉。但是由於現場賽是用linux評測機,所以在現場賽這題應該能用這個方法過。

tle**:

#include

using

namespace std;

#define debug 0

#define online_judge

typedef

long

long ll;

typedef

unsigned

long

long ull;

typedef

unsigned

int uint;

bool

addequll

(ull& lhs, ull rhs)

bool

subequll

(ull& lhs, ull rhs)

struct _uint128

_uint128

(ull _h, ull _l)

explicit

operator

bool()

explicit

operator

ull(

)bool

operator

<

(const _uint128 &rhs)

const

_uint128&

operator+=

(const _uint128 &rhs)

_uint128 operator+(

const _uint128& rhs)

const

_uint128&

operator-=

(const _uint128& rhs)

_uint128 operator-(

const _uint128& rhs)

const

_uint128&

operator

=(uint rhs)

_uint128&

operator*=

(uint rhs)

of =

addequll

(tmp,

(h & ml32)

* rhs)

; h =

(h & mh32)

|(tmp & ml32)

; tmp >>=

32u;

if(of)

addequll

(tmp,

(h >>

32u)

* rhs)

; h =

(h & ml32)|(

(tmp & ml32)

<<

32u)

;return

*this;}

_uint128 operator

*(uint rhs)

const

_uint128 operator-(

)const

_uint128&

diveqmod

(ull& rem, uint x)

_uint128&

operator/=

(uint x)

_uint128 operator

/(uint x)

const

_uint128&

operator

<<=

(uint x)

_uint128 operator

<<

(uint x)};

//typedef ll _uint128;

intmain()

else

} cout <<

"case #"

<< ti <<

": "

<< ans +

1<< endl;

}return0;

}

另外有乙個比較巧妙的方法,不需要用到__int128。我就是用這個方法過掉的這題。

大佬部落格:

我的**:

#include

using

namespace std;

#define debug 0

#define online_judge

typedef

long

long ll;

intmain()

else}}

else

} cout <<

"case #"

<< ti <<

": "

<< ans +

1<< endl;

}return0;

}

2018瀋陽模擬賽 K

這道題很費勁的將所有superme number找了出來,發現317以後就再也沒有該數了 1,2,3,5,7,11,13,17,23,31,37,53,71,73,113,131,137,173,311,317 這個過程過了 很久沒寫字串的題,字串的知識又糊了。輸入一串很長的數,可以用乙個char型...

2018南京ICPC現場賽部分題題解

給你n個石子,下標從1到n,每次最多取連續的k個石子,問最後誰能贏 取完石子的那個人算贏 如果有奇數個石子,那麼a中間取乙個,b拿什麼,a接下去就在對稱的另一邊拿什麼,這樣a贏。如果有偶數個石子,且b 1 那麼a可以在中間取兩個,然後又回到了上述的過程,這樣a贏。如果b 1,那麼石子是偶數個,則b贏...

2023年瀋陽網路賽D題 k短路模板題

題意 給出n個點,m條邊,起始點s,終點t,第k短路,最大代價e 如果第k短路的代價大於e或者沒有第k短路就輸出 whitesnake!否則輸出 yareyaredawa 注意的地方 yareyaredawa沒有感嘆號,多組輸入 思路 k短路模板題 includeusing namespace st...