https://www.programiz.com/online-compiler/61ZybNaUbR5xd
/* // 這裡是原本的暴力模擬版本 (用 vector.erase),時間複雜度太高,所以被註解掉
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N, M, K;
cin >> N >> M >> K;
vector<int> circle(N);
for (int i=0;i<N;i++)
circle[i] = i + 1; // 初始化圈子 [1..N]
int pos = 0;
for (int boom = 1; boom <= K; boom++) {
pos = (pos + M - 1) % circle.size(); // 找到要淘汰的位置
//cout << "淘汰 " << circle[pos] << endl;
if (boom == K) {
int lucky = circle[(pos + 1) % circle.size()]; // 找到最後幸運者
cout << lucky << endl;
return 0;
}
circle.erase(circle.begin() + pos); // 淘汰該人 (O(N))
if (pos == circle.size())
pos = 0; // 如果剛好刪到最後一個,重置 pos
}
}
*/
#include <bits/stdc++.h> // 匯入所有標準函式庫
using namespace std;
// Fenwick Tree (Binary Indexed Tree) 結構
struct Fenwick {
int l; // 陣列大小 (總人數)
vector<int> temp; // Fenwick Tree 的內部陣列 (用來維護活著的人數)
// 建構子:初始化長度 l,並建立一個長度 l+1 的陣列 (1-based)
Fenwick(int l): l(l), temp(l+1,0) {}
// 在位置 i 加上 val (更新 Fenwick Tree)
void add(int i,int val){
for(; i<=l; i+=i&-i){ // i += i&-i 代表跳到下一個負責的節點
temp[i]+=val; // 更新節點值
}
}
// 查詢前 i 個元素的總和 (這裡命名為 all)
int all(int i){
int s=0;
for(; i>0; i-=i&-i) { // i -= i&-i 代表往上跳到父節點
s+=temp[i]; // 累加區間和
}
return s;
}
// 找到第 k 個活著的人 (order statistic)
int begin(int k){
int NotHere=0; // 當前已經確定不包含答案的區間右端點
// mask = 小於等於 l 的最大 2 的冪次
for(int mask=1<<(31-__builtin_clz(l));mask!=0;mask/=2){
int next=NotHere+mask; // 嘗試往右跳 mask
if(next<=l && temp[next]<k){ // 如果這段區間的總和 < k,代表答案在更右邊
k-=temp[next]; // 扣掉這段的數量
NotHere=next; // 更新目前位置
}
}
return NotHere+1; // 回傳第 k 個活著的人的原始編號
}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N,M,K;
cin>>N>>M>>K; // 輸入人數 N、間隔 M、爆炸次數 K
Fenwick fw(N); // 建立一個 Fenwick Tree,大小 N
for(int i=1;i<=N;i++)
fw.add(i,1); // 初始化:每個人都活著 (值=1)
int alive=N; // 當前活著的人數
int pos=0; // 當前炸彈所在位置 (以活著的人數排名為基準,0-based)
for(int boom=1; boom<=K; boom++){ // 模擬 K 次爆炸
pos=(pos+M-1)%alive; // 找到要淘汰的順位 (第 pos 個活著的人)
int eliminated=fw.begin(pos+1); // 找到該順位對應的原始編號
fw.add(eliminated,-1); // 把這個人標記為淘汰 (設為 0)
alive--; // 活著的人數減 1
if(boom==K){ // 如果是第 K 次爆炸
int luckyIndex=(pos%alive)+1; // 下一位 (1-based)
int lucky=fw.begin(luckyIndex); // 找到幸運者的原始編號
cout<<lucky<<"\n"; // 輸出幸運者
return 0; // 結束程式
}
}
}