統計數對 題解

2021-06-09 13:50:23 字數 3155 閱讀 1861

a1350. 數對統計 (羅劍橋)

時間限制:1.0s 記憶體限制:256.0mb

試題**

ioi2012中國國家隊訓練

問題描述

你得到了乙個由 n 個非負整數構成的序列 a。

你需要回答關於這個序列的 q 次詢問。

每次詢問將提供三個整數引數 v,a,b。你需要統計滿足以下三個條件的整數

數對(i, j)的數目:1 <= i <= j <= n, a <= j-i+1 <= b, 並且對於任意i <= k <= j的整數k有a[k] >= v。其中a[k]表示序列a的第k項。

輸入格式

輸入的第一行包括兩個正整數 n, q,分別表示序列 a 的項數和詢問數。 第二行,n 個非負整數。其中第 i 個整數表示 a[i]。

接下來的 q 行,每行 3 個整數 v, a, b,依次表示一次詢問的三個引數。

輸出格式

輸出 q 行,每行乙個非負整數。第 i 行的非負整數表示第 i 次詢問 的答案。

樣例輸入

5 35 3 2 7 4

3 2 3

2 2 5

4 1 1

樣例輸出210

3資料規模和約定

20%的資料滿足,1 <= n <= 1 000, 1 <= q <= 1 000;

40%的資料滿足,1 <= n <= 50 000, 1 <= q <= 50 000;

60%的資料滿足,1 <= n <= 100 000, 1 <= q <= 100 000; 100%的資料滿足,1 <= n <= 300 000, 1 <= q <= 300 000, 0 <= a[i], v <= 1 000, 1 <= a, b <= 200 000.

乍看此題,以為是個資料結構題。使勁想也沒有想出來什麼資料結構可以支援這個,最後亂寫全掛。

之後 orz 了 lyp,覺得這真是一道非常非常好的題目,有非常非常多的值得我所學習的地方。

所以,很容易想到的就是把 a 序列和詢問都按元素為關鍵字逆序排序,離線解決每乙個詢問。

這樣一來,區間的產生就容易多了:對於每乙個詢問,所問的為 a[k] >= v,這就意味著比 v 大的是可以保留的。

所以,每次將大的取出,更新區間集合,然後就可以盡快回答當前關鍵字的詢問,因為區間集合中的每乙個元素都是滿足條件的(不然就是 0 了)。

這樣一來,不用去列舉每乙個區間,這是降低時間複雜度的關鍵。

再者,對於乙個詢問(v, a, b)和乙個合法的區間(s, t),易知其有一需要根據 l1 = b - a + 1 和 l2 = t - s + 1 分情況討論的公式,但是分情況討論不便於維護。

所以如何轉化呢?首先易知,如果對於 i 滿足 s <= i <= t, 則生成滿足條件數對的個數易知其為 (l - i + 1) * (l - i + 2), l = t - s + 1 。

發現了什麼?顯然,如果對於 i, j 滿足 s <= i <= j <= t, 則生成滿足條件數對的個數為 (l - i + 1) * (l - j + 2) - (l - (j + 1) + 1) * (l - (j + 1) + 2), l = t - s + 1 。

證明顯然,只需一減即可知。拆開此式,有 l * l + (3 - i * 2) * l + (i - 3) * i + 2 。記此式為 g(l, i) 。

又因為 s <= i <= t, 所以不討論範圍之外的 i 。又易知每次的詢問即當前所有(合法)區間的 g(l, a) - g(l, b + 1) 值和。

所以當前的任務為:快速求出 σ g(l, a), l >= a 。如何做呢?最容易為所想到的是:維護  σ g(l, 1 .. n), 再得差求得。

觀察 g(l, i), 易得所需要維護的為 σ l * l, σ l, σ count,依次乘以 1, (3 - i * 2), (i - 3) * i + 2 即為 g(l, i) 。

想到了什麼?樹狀陣列。

每次如果要刪除乙個區間(合併時應刪除原區間),就在該長度 l 的字尾陣列中分別減去 l * l, l, 1 即可。新增同。

如何求 g(l, i)? 求出 1 .. i - 1 的區間和,再用乙個總和減去即可。

如何回答詢問?求出 g(l, a) - g(l, b + 1), 再分別存入每一次詢問中即可。

至此,該問題已解決。

code :

#include #include #include #include #include #include typedef unsigned uint;

typedef long long int64;

typedef unsigned long long uint64;

#define swap(a, b, t) ()

#define max(a, b, t) ()

#define min(a, b, t) ()

#define maxn 310000

#define maxv 1100

#define link(e, i, j) (next[++ adj] = e[i], to[e[i] = adj] = j)

int n, m, u, v, a, b, adj = 1;

int ufs[maxn], x[maxn << 1], y[maxn << 1], size[maxn];

int e1[maxv], e2[maxv], next[maxn << 1], to[maxn << 1];

int64 s[maxn], s1[maxn], s2[maxn], ans[maxn];

int64 t, t1, t2;

int find(int x)

void alter(int p, int d)

int64 query(int p)

int main()

for (int i = 1; i <= m; ++ i)

for (int i = 1000; i >= 0; -- i)

for (int e = e2[i]; e; e = next[e])

ans[to[e]] = (query(x[e] - 1) - query(y[e])) >> 1;

}for (int i = 1; i <= m; ++ i)

printf("%i64d\n", ans[i]);

return 0;

}

統計陣列的逆序對

題目描述 在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。題目 思路 這個最簡單的當然是直接統計了,演算法時間複雜度為n 2,氣泡排序都可以。如果想進一步減少時間複雜度的話,可以考慮用遞迴的方法,比如使用歸併排序。下面貼 i...

統計數字 快速排序題解

某次科研調查時得到了n個自然數,每個數均不超過1500000000 1.5 109 已知不相同的數不超過10000個,現在需要統計這些自然數各自出現的次數,並按照自然數從小到大的順序輸出統計結果。輸入第1行是整數n,表示自然數的個數 第2 n l每行乙個自然數。輸出共m行 m為n個自然數中不相同數的...

LeetCode 統計陣列中逆序對的個數

題目描述 在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對.輸入乙個陣列,求出這個陣列中 的逆序對的總數p.並將p對1000000007取模的結果輸出.即輸出p 1000000007.note 2 n 50000 example 1 input nums 9,6,8,5...