給出乙個陣列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.書中列出了一種演算法,暫且叫遞迴法,這可以自己...