題目大意:給定初始人數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...