HZNUOJ ACM實驗室暑期考核 F B_M的斐波那契 線段樹/樹狀陣列
阿新 • • 發佈:2020-08-26
題意
給一個長度為\(n\) 的\(a\) 陣列,初始時\(a_i = 1\) ,有兩個操作
- \(l-r\) 區間內\(a_i += 1\)
- 詢問\(l-r\) 區間\(a_i\) 作為斐波那契數列下標的數值和是否$ \geq k \quad$ ($ k \leq 10^{10})$
分析
如果直接線段樹區間修改顯然是做不到的,不妨換一種思路
注意到\(f_{50} \geq 10^{10}\) ,這意味著一個點一旦被加了超過\(50\) 次,就沒必要修改這個點了。
這樣就考慮怎麼高效的刪除這些不需要的點。
考慮用set 存放\(1-n\) 的下標,一旦某個下標的斐波那契數列項數超過了\(50\) 就刪除這個節點。這樣一來
就是一個單點修改(注意一個點最多被修改50次) ,區間求和
於是就可以用樹狀陣列或者線段樹維護了
ll fib[55] = { 0, 1 }; int n, m; int num[maxn << 2]; ll sum[maxn << 2]; set<int> st; void pushUp(int i) { sum[i] = sum[i << 1] + sum[i << 1 | 1]; } void update(int pos, int l, int r, int i) { if (l == pos && r == pos) { num[i]++; sum[i] = fib[num[i]]; if (num[i] == 50) st.erase(pos); return; } int mid = l + r >> 1; if (pos <= mid) update(pos, l, mid, i << 1); else update(pos, mid + 1, r, i << 1 | 1); pushUp(i); } int res; void query(int l, int r, int i, int L, int R) { if (L <= l && R >= r) { res += sum[i]; return; } int mid = l + r >> 1; if (L <= mid) query(l, mid, i << 1, L, R); if (R > mid) query(mid + 1, r, i << 1 | 1, L, R); } void range_update(int l, int r) { auto begin = st.lower_bound(l); auto end = st.upper_bound(r); //這裡用了upper -1就一定是範圍裡了 if (end != st.end()) end--; for (auto it = begin; it != end; it++) { update(*it, 1, n, 1); } } int main() { for (int i = 2; i < 55; i++) fib[i] = fib[i - 1] + fib[i - 2]; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) update(i, 1, n, 1), st.insert(i); while (m--) { int op; scanf("%d", &op); if (op == 1) { int l, r; scanf("%d%d", &l, &r); range_update(l, r); } else { int l, r; ll k; scanf("%d%d%lld", &l, &r, &k); res = 0; query(1, n, 1, l, r); puts(k <= res ? "YES" : "NO"); } } return 0; }