#53896: 答案程式(都有註解能看)


snowest (snow_west_tw)


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;                   // 結束程式
        }
    }
}