--- tags: 基礎算法 title: 倍增 --- :::info #### [ECPC16 PJ](https://codeforces.com/gym/101147/problem/J) #### [TIOJ 1341](https://tioj.ck.tp.edu.tw/problems/1341) ::: :::success # 快速冪 Exponentiating by Squaring ## 問題 - 求$n^m$ - ex: 求$7^{13}$ ## 實作 - 將指數轉二進制 binary(13) = 1101 - 按照二進制dp計算$7^{[0,3]}$ - 用計算好的冪次表對照二進制的1和0計算結果 - 複雜度:$O(\log m)$ ```cpp= int pow(n, m) { int ret = 1; for (int power = m; power; power >>= 1, n *= n) { ret *= (power & 1?n:1); } return ret; } ``` ```cpp= int mod = 1000000007; int mi(int a, int b) { // return a^b if(b == 0) { return 1; } else { int k = mi(a, b/2); if(b % 2 == 1)return k*k%mod*a %mod; else return k*k %mod; } } ``` ::: :::info #### [TOJ 510](https://toj.tfcis.org/oj/pro/510/) ::: :::success # 倍增 Binary Lifting ## 問題 - $N$張牌洗$K$次,每次照給定陣列$F$洗牌,求結果 ## 模擬解 - 迴圈暴力模擬 - 複雜度:$O(K)$ ## 倍增解 - 把$K$轉成二進制 - 用$F$初始化倍增陣列shuffle - shuffle$[n][m]$ -> 第n個位置的卡牌在洗$2^m$個回合後的位置 - 遞迴式:shuffle$[n][m]$ = shuffle$[$shuffle$[n][m-1]][m-1]$ - 用shuffle陣列對照K的二進制跳指定回合 - 複雜度:$O(\log K)$ ```cpp= //很久遠的AC code //上面的shuffle在這裡叫dp #include <cstdio> #include <vector> using namespace std; vector<vector<int>> dp; vector<int> temp, binary; int B[300000], ans[300000]; //B -> begin void decimalToBinary(long long x) { //轉二進制 while (x > 0) { binary.push_back(x%2); x /= 2; } } int main(void) { int N, cur; long long K; scanf("%d", &N); temp.resize(N); for (int i=0; i<N; i++) { scanf("%d", &B[i]); } for (int i=0; i<N; i++) { scanf("%d", &temp[i]); } dp.push_back(temp); scanf("%lld", &K); decimalToBinary(K); int len = binary.size(), times = len-1; for (int i=1; i<=times; i++) { //建構倍增陣列 for (int o=0; o<N; o++) { temp[o] = dp[i-1][dp[i-1][o]-1]; } dp.push_back(temp); } for (int i=0; i<N; i++) { //用倍增陣列對照二進制模擬過程 cur = i; for (int o=0; o<len; o++) { if (binary[o]) cur = dp[o][cur] - 1; } ans[cur] = B[i]; } printf("%d", ans[0]); for (int i=1; i<N; i++) { printf(" %d", ans[i]); } printf("\n"); return 0; } //這題輸出要絕對準確,不能多換行或空格 ``` ``` 5 1 2 3 4 5 3 1 4 5 2 1 2 5 1 3 4 ``` ::: :::info #### [TIOJ 2172](https://tioj.ck.tp.edu.tw/problems/2172) ::: :::success # 最近(低)共同祖先 Lowest Common Ancestor ## 定義 - 兩個node的所有祖先中離根最遠,最深的一個 ![](https://i.imgur.com/Yu1u3yp.png) | n | f[n][0] | f[n][1] | f[n][2] | | --- | ------------ | ------------ | ------------ | | 0 | 1 | 4 | -1 | | 1 | 4 | -1 | -1 | | 2 | 9 | 5 | -1 | | 3 | 9 | 5 | -1 | | 4 | -1 | -1 | -1 | | 5 | 4 | -1 | -1 | | 6 | 1 | 4 | -1 | | 7 | 9 | 5 | -1 | | 8 | 5 | 4 | -1 | | 9 | 5 | 4 | -1 | - f[節點編號n][節點往上跳第$2^m$次方] -> n往上跳2^m次方的祖先的編號 - 遞迴式:f$[n][m]$ = f$[$f$[n][m-1]][m-1]$ - d[節點編號n] -> n的深度 ```cpp= vector<int> B; void binary(int n) { B.clear(); while(n > 0) { B.push_back(n%2); n /= 2; } reverse(B.begin(), B.end()); } int LCA(int a, int b) { //1° 較深節點往上跳到較淺節點的深度 if (d[a] < d[b]) swap(a, b); binary(d[a] - d[b]); for (int i=0; i<B.size(); i++) { if (B[i]) a = f[a][i]; } if (a == b) return a; //2° 兩節點同時往上跳到LCA下一層 else { for (int i = log(N); i >= 0; i--) { if (f[a][i] != f[b][i]) { a = f[a][i]; b = f[b][i]; } } } return f[a][0]; } cin >> a >> b; cout << LCA(a, b); return 0; ``` :::