HDU 3874 Necklace 樹狀陣列 + 離線處理
阿新 • • 發佈:2019-02-10
/** * 樹狀陣列 + 離線: * 看人題解的,思路: 把詢問區間[l, r]存下後按r從小到大排序 * 用陣列hash[i]記錄數字i最後一次出現的位置。用變數curR從1開始每次 * 往右掃到每個詢問區間的r。關鍵在於沒掃到新的一個數的時候,如何更新 * 維護這個樹狀陣列。把每次詢問的答案記錄到ans陣列。 * 具體: 當前數num[curR]如果出現過,則最後一次出現位置為hash[num[curR]] * 更新update(hash[num[curR]], -num[curR]),每次新的數不管是不是出現過, * 都要update(curR, num[curR]), 因為詢問的規則是重複的數只記錄一次,那麼 * 在前一條語句update(hash[num[curR]], -num[curR])意思是把之前維護的樹狀陣列 * 在hash[num[curR]]的位置減去-num[curR]。 * 為什麼要減? * 每次詢問的答案是get_sum(r) - get_sum(l - 1) 也就是前r和減去前(l-1)和 * 可是要保證重複的數只計算一次,這樣在用curR掃到當前詢問的r的時候,掃到重複數的 * 時候就要減去之前的,並在當前出現的位置加上當前的數。 這樣就能保住字首和正確維護了。 */ #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <string> #include <queue> #include <map> #include <vector> #include <algorithm> #define DEBUG 0 #define INF 0x3fffffff #define OUTSTARS printf("*****************************\n"); #define MAXN 50005 typedef long long LL; using namespace std; LL c[MAXN], ans[200005]; int num[MAXN]; struct Q { int l, r, NO; bool operator < (const Q &a) const { return r < a.r; } } queries[200005]; int n, m; map<int, int> hash; int lowbit(int x) { return x & (-x); } void update(int pos, int val) { for(int i = pos; i <= n; i += lowbit(i)) c[i] += val; } LL query(int pos) { LL ret = 0; for(int i = pos; i > 0; i -= lowbit(i)) ret += c[i]; return ret; } int main() { int t; scanf("%d", &t); while(t --) { scanf("%d", &n); memset(c, 0, sizeof(c)); for(int i = 1; i <= n;i ++) scanf("%d", &num[i]); scanf("%d", &m); for(int i = 1; i <= m; i ++) { scanf("%d%d", &queries[i].l, &queries[i].r); if(queries[i].l > queries[i].r) swap(queries[i].l, queries[i].r); queries[i].NO = i; } sort(queries + 1, queries + 1 + m); hash.clear(); int curR = 1; for(int i = 1; i <= m; i ++) { while(curR <= queries[i].r) { if(hash[num[curR]] != 0) { update(hash[num[curR]], -num[curR]); } hash[num[curR]] = curR; update(curR, num[curR]); curR ++; } ans[queries[i].NO] = query(queries[i].r) - query(queries[i].l - 1); } for(int i = 1; i <= m; i ++) printf("%I64d\n", ans[i]); } return 0; }