生活随笔
收集整理的這篇文章主要介紹了
【莫队】区间众数(Codeforces Round #716 (Div. 2) D)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
D. Cut and Stick
(賽后補(bǔ)題)借本題學(xué)習(xí)莫隊算法以及區(qū)間眾數(shù)的求法
題意:對于整型數(shù)組,每次詢問[L,R][L,R][L,R]區(qū)間問最少分為多少個子序列,使得每個子序列的眾數(shù)xxx的個數(shù)cntxcnt_xcntx?不大于 ?len2?\left \lceil \frac{len}{2} \right \rceil?2len??,lenlenlen表示子序列的長度
思路:對于每次詢問只需知道詢問區(qū)間內(nèi)的眾數(shù)xxx的個數(shù)即可,最優(yōu)解即為將其余的非眾數(shù)盡可能與更多的xxx組合為一個子序列,而剩下的xxx每個自為一個子序列
給一個不嚴(yán)格的證明,圖中有aaa個眾數(shù)與bbb個非眾數(shù),法①將全部的非眾數(shù)放在同一個子序列中,此時該子序列最多匹配b+1b+1b+1個眾數(shù),而剩余的眾數(shù)則需單獨(dú)出現(xiàn);法②將b個非眾數(shù)分為兩部分(c+d=bc+d=bc+d=b),兩部分分別匹配c+1c+1c+1和d+1d+1d+1個眾數(shù)(c+1+d+1=c+d+2=b+2c+1+d+1 = c+d+2 = b+2c+1+d+1=c+d+2=b+2)此時比法①多匹配了一個眾數(shù),然而由于被分為兩部分,自然地也多產(chǎn)生了一個子序列,因此兩者是等效的,可見只要將盡可能多的眾數(shù)匹配給非眾數(shù),無論非眾數(shù)分為多少塊其效果是等效的
因此問題便轉(zhuǎn)化為求區(qū)間眾數(shù)的個數(shù)問題。
數(shù)組長度:nnn,詢問次數(shù):mmm
若采用暴力求解,每次詢問計算眾數(shù)的個數(shù)的時間復(fù)雜度為O(n)O(n)O(n),總時間復(fù)雜度為O(m?n)O(m*n)O(m?n),會超時
采用莫隊算法優(yōu)化,可將每次詢問的平均時間復(fù)雜度降為O(n)O(\sqrt{n})O(n?)
莫隊步驟:
將數(shù)組分為大小為n\sqrt{n}n?的塊將詢問順序按下面的規(guī)則排序
首先按照LLL所在的塊號排序,塊號越小越靠前(升序)
若LLL所在的塊號相同,則按RRR所在的塊號升序接著定義兩個函數(shù)add()add()add()和del()del()del()分別用于添加和刪除元素,用于在上一個詢問的區(qū)間基礎(chǔ)上增刪元素得到當(dāng)前詢問的區(qū)間,每次調(diào)用增刪函數(shù)的關(guān)鍵是動態(tài)地更新眾數(shù)的狀態(tài)
維護(hù)眾數(shù)狀態(tài):
采用mapmapmap映射存儲每個數(shù)出現(xiàn)的次數(shù),采用數(shù)組aaa記錄有多少個數(shù)出現(xiàn)了iii次,當(dāng)前的眾數(shù)的個數(shù)即為數(shù)組a[i]>0a[i]>0a[i]>0的最大的iii,每次增刪實(shí)時更新兩者的數(shù)據(jù)即可
#include<bits/stdc++.h>
#define debug1(x) cout<<#x<<":"<<x<<endl
#define fastio() ios_base::sync_with_stdio(0);cin.tie(0)
typedef long long ll
;
typedef unsigned long long ull
;
const int N
= (int)3e5+100;
const int RN
= sqrt(N
)+10;
const int MOD
= 10000;
using namespace std
;
int a
[N
];
map
<int,int> mp
[RN
];
int n
,m
,block
;
int cnt
[N
],sum
[N
],ans
[N
],max_cnt
= 0;
struct query
{int id
;int l
;int r
;
}q
[N
];bool cmp(query a
, query b
)
{int al
= a
.l
/ block
, bl
= b
.l
/ block
;int ar
= a
.r
/ block
, br
= b
.r
/ block
;if(al
!= bl
) return al
< bl
;return ar
< br
;
}void add(int e
)
{sum
[cnt
[e
]]--;cnt
[e
]++;sum
[cnt
[e
]]++;max_cnt
= max(max_cnt
, cnt
[e
]);
}void del(int e
)
{sum
[cnt
[e
]]--;if(cnt
[e
] == max_cnt
&& sum
[cnt
[e
]] == 0) max_cnt
--;cnt
[e
]--;sum
[cnt
[e
]]++;
}int main()
{cin
>>n
>>m
;block
= sqrt(n
);for(int i
= 1; i
<= n
; i
++) scanf("%d",&a
[i
]);int l
, r
;for(int i
= 0; i
< m
; i
++){scanf("%d%d",&l
,&r
);q
[i
].id
= i
;q
[i
].l
= l
;q
[i
].r
= r
;}sort(q
, q
+ m
, cmp
);int cl
, cr
, width
;cl
= cr
= 0; add(a
[0]);for(int i
= 0; i
< m
; i
++){l
= q
[i
].l
, r
= q
[i
].r
;while(cr
< r
) add(a
[++cr
]);while(cl
> l
) add(a
[--cl
]);while(cr
> r
) del(a
[cr
--]);while(cl
< l
) del(a
[cl
++]);width
= r
- l
+ 1;ans
[q
[i
].id
] = max(2 * max_cnt
- width
, 1);}for(int i
= 0; i
< m
; i
++) printf("%d\n", ans
[i
]);return 0;
}
總結(jié)
以上是生活随笔為你收集整理的【莫队】区间众数(Codeforces Round #716 (Div. 2) D)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。