初級莫隊演算法的詳解(附加乙個例題)

2021-08-22 06:07:09 字數 1843 閱讀 2371

題目大意:給你n(1 ≤ n ≤ 30000)個數(1 ≤ ai ≤ 10^6),q(1 ≤ q ≤ 200000)個詢問,每個詢問有l,r兩個數,問這個區間內有多少個不同的數。

input51

1213

3152

435output32

3

最暴力的方法就是,先從【1,5】區間乙個乙個查詢,在從【2,4】區間乙個乙個查詢……

但是這樣可能會出現n^2的時間複雜度,所以這樣是肯定不行的。

莫隊核心思想(包括帶修莫隊也是這樣,只不過是加入和修改的部分而已)

我們可以先查詢【1,5】區間,記錄每乙個元素的個數,再拓展到【2,5】區間,這樣是只是少了1號元素,再拓展到【2,4】區間,少了5號元素,所以【2,4】的答案就出來了,以此類推可以求到所有的答案。

不過這樣是肯定不行的,因為區間的順序不同所消耗的時間是不同的,也有可能會超時。

此時我們要對所有的區間進行排序,使得所需要查詢的次數降低,以此來降低時間複雜度。

不知道我們該如何排序呢?

我們要利用分塊的思想(只是不清楚為啥要這樣,可以降低時間複雜度),可以將時間複雜度降到o(n√n)。

我們的排序是按照左段點所在的塊為第一關鍵字,以右端點為第二關鍵字(這點很重要)。

排完序後從左往右處理詢問(離線),過程就是上述的核心思想部分。

基本上莫隊的做法就是上述部分,只不過不同的題目略有不同而已。

下面是每乙個分部:

block=sqrt(n*1.0);
1.塊的大小:一般是sqrt(n),n為陣列長度。

bool cmp(node f1,node f2)

2.對查詢的排序

3.如何通過加1或-1,從上乙個區間變化到本區間。

下面是詳細**:

#include

#include

#include

#include

using

namespace

std;

#define mem(a,b) memset(a,b,sizeof(a))

const

int maxn=30000+10;

const

int max1=1e6+9;

typedef

long

long ll;

int a[maxn];

int inq[max1],sum[maxn*10];

int block,ans;

struct node

edge[maxn*10];

bool cmp(node f1,node f2)//排序

//每道不同的題目思想不同,下面兩個函式的內容也各不相同

void up(int x)//擴充區間

void down(int x)//縮小區間

int main()

sort(edge,edge+m,cmp);

int l=1;//初始左右區間

int r=0;

ans=0;//答案

for(int i=0; iint s=edge[i].s,t=edge[i].t;

while(r//區間擴充

while(l>s)//區間擴充

while(r>t)//區間縮小

while(l//區間縮小

sum[edge[i].id]=ans;//記錄答案

}for(int i=0; iprintf("%d\n",sum[i]);

}return

0;}

ACM bitset 的基本用法 (附帶乙個例題)

bitset 100 a代表你定義了乙個長度為 100 的二進位制串。需要注意的是 bitset 的 中的數必須要是常數不能是變數,因為他需要在編譯的時候確定大小,這一定也讓我肯定了它內部是位域組織的。bitset 10000 d 100 7 d n set 全部置1 cout 題目描述 一共有 n...

遺傳演算法的乙個例子

遺傳演算法的手工模擬計算示例 為更好地理解遺傳演算法的運算過程,下面用手工計算來簡單地模擬遺傳演算法的各 個主要執行步驟。例 求下述二元函式的最大值 1 個體編碼 遺傳演算法的運算物件是表示個體的符號串,所以必須把變數 x1,x2 編碼為一種 符號串。本題中,用無符號二進位制整數來表示。因 x1,x...

複習基礎知識的時候改寫的乙個例題

文件建立日期 2010 02 15 以下的輸出結果可以直接用到數學的查表求值 the result is 0.0175 0.0349 0.0523 0.0698 0.0872 0.1045 0.1219 0.1392 0.1564 0.1736 0.1908 0.2079 0.2250 0.2419...