主元素 線段樹

2021-09-13 12:21:06 字數 3905 閱讀 7843

給出乙個陣列a[1…n],再給出m個詢問,第i個詢問的格式是:li、ri,表示的意義是:a[li]至a[ri]這個區間中,是否存在「主元素」,如果存在則輸出「yes」和主元素,否則輸出「no」。

下面解釋「主元素」:在乙個包含x個元素的區間裡面,如果有個元素出現的次數嚴格大於x/2,那麼該元素就是區間的主元素。

第一行,n和c。3 <= n <= 300000,1 <= c <= 10000。

第二行,n個正整數,第i個正整數表示a[i]。 其中1<=a[i]<=c。

第三行,乙個整數m。1 <= m <= 10000。

接下來有m行,第i行給出:li和ri。1 <= li <= ri <= n。

共m行,每行對應乙個詢問。

10 3

1 2 1 2 1 2 3 2 3 3

81 2

1 31 4

1 52 5

2 66 9

7 10

no

yes 1

noyes 1

noyes 2

noyes 3

本題涉及到了區間問題,首先考慮使用線段樹這個資料結構

但是本題並沒有涉及區間和、區間最大等經典線段樹的問題,而是涉及元素的數量問題

那我們可不可以改造線段樹來適應這一道題呢?當然可以

首先來思考一下主元素的特點:

1、在乙個包含x個元素的區間裡面,有乙個元素出現的次數嚴格大於x/2

2、這個區間裡可能沒有主元素

再仔細分析,可以發現:

設有兩個區間[l1,r1]和[l2,r2],(l1<=r1<=l2<=r2),那麼這兩個區間的主元素有以下關係:

1、若[l1,r1]和[l2,r2]都沒有主元素,那麼區間[l1,r2]也沒有主元素,證明:

∵[l1,r1]和[l2,r2]都沒有主元素

∴設出現次數最多的數x分別在兩個區間內出現了(r1-l1)/2次和(r2-l2)/2次

∴x在[l1,r2]內出現了(r2-l2+r1-l1)/2次,即(r2-l1)/2次

∵(r2-l1)/2=(r2-l1)/2

∴若[l1,r1]和[l2,r2]都沒有主元素,那麼區間[l1,r2]也沒有主元素

2、若[l1,r1]和[l2,r2]都有相同的主元素x,那麼區間[l1,r2]的主元素為x,證明:

∵[l1,r1]和[l2,r2]都有主元素x

∴x分別在兩個區間內最少出現了(r1-l1)/2+1次和(r2-l2)/2+1次

∴x在[l1,r2]內出現了(r2-l2+r1-l1)/2+2次,即(r2-l1)/2+2次

∵(r2-l1)/2+2>(r2-l1)/2

∴若[l1,r1]和[l2,r2]有相同的主元素x,那麼區間[l1,r2]的主元素為x

3、若[l1,r1]和[l2,r2]都有主元素且分別為x和y(x≠y),那麼區間[l1,r2]有可能沒有主元素,有可能主元素為x,有可能主元素為y此時我們就歸納出了維護線段樹的做法,但是對於第三部的具體實現,仍然還沒有乙個確切的想法,

我們不妨嘗試列舉區間[l1,r2],尋找x的個數和y的個數,通過比較確定主元素

這時我們又發現,如果直接暴枚,但詢問次數多且r2-l1比較大時,那是肯定要tle(time limit exceed 超時)的,降低暴枚的時間復!雜度,我們很容易想到,用二分才能將o(n)降到o(log2n)。

問題就在於:怎麼二分

有一種巧妙的做法可以幫助我們:

首先開乙個pos陣列,我們用pos陣列儲存每一種元素序列中的所有位置

再開乙個head陣列,記錄每一種元素在pos中開始的位置

為了方便,開sum陣列,記錄每一種元素的數量

然後我們就能輕易求出tail的值,在head和tail的範圍內二分列舉在[l1,r2]內

最左邊x出現的位置在pos中的位置pos1

最右邊x的位置在pos中的位置pos2

x的數量為num=pos2-pos1

這麼一來,這道題就完成了

#include

using

namespace std;

int n,c,m,a[

300005

],b[

10005

],head[

10005

],pos[

300005

],x,y;

struct nodetree[

1200020

],nul/*表示空*/

,ans;

intsearchmain

(int le,

int ri,

int x)

//原理:二分查詢

} start=head[x]

-b[x]+1

,end=head[x]

;/*二分查詢右端點*/

while

(start<=end)

else

end=middle-1;

}return high-low+1;

}void

toper

(node &rt,

int lmain,

int rmain,

int left,

int right)

//維護操作

else

if(temp2>

(right-left+1)

/2)/*--------------------------*/}}

void

build

(int k,

int l,

int r)

int mid=

(l+r)/2

;build

(k*2

,l,mid)

;build

(k*2+1

,mid+

1,r)

;toper

(tree[k]

,tree[k*2]

.mainnum,tree[k*2+

1].mainnum,l,r);}

node qaf

(int k,

int l,

int r,

int x,

int y)

intmain()

//-------位置查詢表-------

for(

int i=

2;i<=c;i++

) head[i]

=head[i-1]

+b[i-1]

;for

(int i=

1;i<=n;i++

)//-------------------------

build(1

,1,n);

cin>>m;

for(

int i=

1;i<=m;i++

)return0;

}

尋找主元素

如果乙個陣列a 1.n 中超過半數的元素都相同時,該陣列被稱為含有主元素。演算法思想 利用快速排序的思想,如果這個陣列存在主元素,則它一定為排序後的中位數。但問題是,我們要設計o n 演算法,我們知道,排序的最優時間複雜度是o nlogn 所以我們需要借助其他的方法來完成這個問題。我們可以想到快速排...

搜尋主元素

題目描述 當陣列中每個元素出現的次數大於len 2時,該陣列的主元素就是就是該元素。演算法的設計思想 演算法的策略是從前往後掃瞄陣列元素,標記出乙個可能成為主元素的元素num。然後重新計數,確認num是否是主元素。演算法可分為兩步 1 選取候選的主元素 依次掃瞄所給陣列中的每個整數,將第乙個遇到的整...

主元素演算法

1.演算法描述 演算法分析2.26 大小為n的陣列a,其主元素是乙個出現超過n 2次的元素 從而這樣的元素最多只有乙個 例如,陣列 3,3,4,2,4,4,2,4,4只有乙個主元素4 3,3,4,2,4,4,2,4沒有主元素 求出主元素,沒有請指出 2.書中列出了一種演算法,暫且叫遞迴法,這可以自己...