Nowcoder-小樂樂學數學(樹狀陣列+離線詢問)
阿新 • • 發佈:2021-08-09
今天學長剛剛總結的一類題型
給出了兩道例題,一道是CF220B
另一個是牛客的這道;
題意(CF220B):
每次詢問區間[L,R]有多少個數x出現了剛好x次
題意(牛客):
每次詢問[L,R]有多少數字,a[i]滿足和區間內的其他數都互質
Trick:
自己總結了一下這樣的題的一個算是一個通用的trick吧,首先是對於多次詢問的題目 ,
然後因為我們需要把詢問離線到端點上(具體操作看程式碼就懂),所以每次詢問所涉及到的數字必須是在區間[L,R]內的,就是說區間外的數並不對區間內產生直接的影響,
然後一般是詢問區間的內的點對這類問題,因為找 點對 比較好的入手的方式就是看每個點的貢獻,但是每次詢問又不能掃一遍區間,所以我們只掃一遍區間,在端點上記錄詢問資訊
思路(牛客):
Q1:考慮一個數需要滿足什麼樣的條件和區間所有的數都互質
A1: 這個質因子分解後和所有的數都沒有共同的質因子
那麼首先我們需要在logn的時間複雜度內求出一個數的所有質因子(因為需要對每一個數都要質因子分解)
Q2:對於當前的數x,質因子分解後得到p1,p2,p3怎麼計算貢獻
A2:假設我們知道p1,p2,p3上一次出現的位置即[1,i-1],找一個最大值ma,那麼區間[ma+1,i]這一段的貢獻一定是1
Q3:怎麼消除一個位置的貢獻
A3:消除和新增這裡都選擇用樹狀陣列實現,維護差分序列,維護單點修改,區間查詢
操作細節:
假設當前操作到的數字是a[i],質因子分解後是p1,p2,p3
首先找到Q2的ma,然後單點修改兩個點的值
然後
找到p1,p2,p3上一次出現的位置 (如果是第一次消除這個位置的貢獻,那麼這個位置的貢獻一定是1) 單點修改值
p1,p2,p3被修改為1的時候他們都各自有一個ma1,ma2,ma3被修改為-1,消除貢獻的時候要把ma1,ma2,ma3改回0
具體操作看程式碼
CODE:
#include "bits/stdc++.h" using namespace std; typedef long long ll; const int maxn = 2e5 + 7; #define mst(x, a) memset(x, a, sizeof(x)) #defineView Coderep(i, a, b) for (int i = (a); i <= (b); ++i) int n, m, a[maxn], ans[maxn]; vector<pair<int, int>> e[maxn]; int pos[maxn], pre[maxn], flag[maxn]; int t[maxn]; int lowbit(int x) { return x & (-x); } void add(int x, int op) { if (x == 0) return; while (x <= n) { t[x] += op; x += lowbit(x); } } int query(int x) { int res = 0; while (x > 0) { res += t[x]; x -= lowbit(x); } return res; } int xx, p[maxn], mi[maxn], vis[maxn]; void oula() { for (int i = 2; i < maxn - 2; i++) { if (vis[i] == 0) p[++xx] = i, mi[i] = i; for (int j = 1; j <= xx && i * p[j] < maxn - 2; j++) { vis[i * p[j]] = 1; mi[i * p[j]] = p[j]; if (i % p[j] == 0) break; } } } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); oula();/*預處理出每個數的最小質因子*/ while (cin >> n >> m) { rep(i, 1, n) cin >> a[i], t[i] = pos[i] = flag[i] = pre[i] = 0; rep(i, 1, n) e[i].clear(); rep(i, 1, m) { int x, y; cin >> x >> y; /*將詢問離線到端點*/ e[y].push_back({x, i}); } for (int i = 1; i <= n; i++) { /*logn的質因子分解*/ int t = a[i]; set<int> s; while (t > 1) { int mm = mi[t]; s.insert(mm); t /= mm; } /*找出距離i最近的一個質因子(其最後一次出現的位置)*/ int ma = 0; for (auto num : s) ma = max(ma, pos[num]); /* 消除 相同質因子的貢獻 */ for (auto num : s) { if (flag[pos[num]] == 0) { add(pos[num], -1); if (pre[pos[num]]) add(pre[pos[num]], 1); flag[pos[num]] = 1; } } /*更新每個質因子最後一次出現的位置*/ for (auto num : s) pos[num] = i; add(i, 1); pre[i] = ma; add(ma, -1); for (auto fr : e[i]) ans[fr.second] = query(i) - query(fr.first - 1); } rep(i, 1, m) cout << ans[i] << endl; } return 0; } /* */