P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)
P4062 [Code+#1]Yazid 的新生舞會
杭電多校懂得都懂
Code1
分治
比較喜歡分治的做法,非常好寫。skylee大佬題解
首先對于任何一個區間來說,由于兩個端點不確定性非常難以一次性統計多組區間,因為它們沒有相似之處。
考慮分治,花費log?\loglog的代價使得當前考慮的區間必須經過mid\text{mid}mid,意味著當前區間左端點必須在[l,mid][\text{l},\text{mid}][l,mid]區間內部,而右端點必須在[mid+1,r][\text{mid}+1,\text{r}][mid+1,r]區間內部,這樣的區間特殊在一定經過mid使得可以先預處理左端點的一些信息,然后枚舉右端點一次性統計多個區間。
首先枚舉可能作為區間絕對眾數的數v\text vv,然后考慮哪些上述區間能使得該數作為區間的絕對眾數。
vl,vr\text{vl},\text{vr}vl,vr分別是可能使v\text vv作為區間[vl,vr][\text{vl},\text{vr}][vl,vr]的絕對眾數。
cntvl\text{cnt}_{\text {vl}}cntvl?:vl→mid\text{vl}\to \text{mid}vl→mid 數字v\text vv出現的次數。
cntvr\text{cnt}_{\text {vr}}cntvr?:mid+1→vr\text{mid}+1\to \text {vr}mid+1→vr 數字v\text vv出現的次數。
cntvr+cntvl>12[r?l+1]\text{cnt}_{\text {vr}}+\text{cnt}_{\text {vl}}>\frac{1}{2}[r-l+1]cntvr?+cntvl?>21?[r?l+1]
從上面式子可以得出
2cntvl+l?1>r?2cntvr2\text{cnt}_{\text {vl}}+l-1>r-2\text{cnt}_{\text {vr}}2cntvl?+l?1>r?2cntvr?
考慮枚舉vr\text{vr}vr,我們只需要統計有哪些左端點vl\text{vl}vl滿足上面式子即可,顯然可以預處理+前綴和一次性統計。
還有一個問題就是哪些數可能是區間的絕對眾數?
如果一個數vvv是區間[vl,vr][\text{vl},\text{vr}][vl,vr]的絕對眾數,那么它一定是區間[vl,mid][\text{vl},\text{mid}][vl,mid]以及[mid+1,vr][\text{mid}+1,\text{vr}][mid+1,vr]的絕對眾數,于是可以提前預處理。
顯然能夠滿足上述條件作為區間絕對眾數的種類不會很多。上面大佬題解說是log?\loglog量級的。
時間復雜度O(nlog?2n)O(n\log ^2n)O(nlog2n)
#include<bits/stdc++.h> using namespace std; using ll=long long; template <class T=int> T rd() {T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg; } const int N=500010; int a[N],n; ll ans; int b[N],cnt; int mp[N],in[N]; int num[2*N]; void solve(int l,int r) {if(l==r) return ans++,void();int mid=l+r>>1;solve(l,mid),solve(mid+1,r);cnt=0;for(int i=mid;i>=l;i--){mp[a[i]]++;if(mp[a[i]]>(mid-i+1)/2){if(in[a[i]]) continue;in[a[i]]=1;b[++cnt]=a[i];}}for(int i=l;i<=mid;i++) mp[a[i]]--;for(int i=mid+1;i<=r;i++){mp[a[i]]++;if(mp[a[i]]>(i-mid)/2){if(in[a[i]]) continue;in[a[i]]=1;b[++cnt]=a[i];}}for(int i=mid+1;i<=r;i++) mp[a[i]]--;for(int i=l;i<=r;i++) in[a[i]]=0;//for(int i=1;i<=cnt;i++) cout<<b[i]<<" \n"[i==cnt];for(int i=1;i<=cnt;i++){int cur=0;int L=3*n,R=0;for(int j=mid;j>=l;j--){if(a[j]==b[i]) cur++;num[2*cur+j-1]++;L=min(L,2*cur+j-1);R=max(R,2*cur+j-1);}for(int i=R;i>L;i--) num[i-1]+=num[i];cur=0;for(int j=mid+1;j<=r;j++){if(a[j]==b[i]) cur++;ans+=(num[max(L,j-2*cur+1)]);}for(int i=R;i>=L;i--) num[i]=0;}} int main() {n=rd();rd();for(int i=1;i<=n;i++) a[i]=rd();solve(1,n);printf("%lld\n",ans);return 0; }Code2
OMG_wc大佬題解
Zechariah大佬題解
其實賽時的將此問題轉化成了下面的問題:如何求小于一段公差為1的等差數列的個數?
- 求在a1→na_{1\to n}a1→n?中求小于xxx的個數,小于x+1x+1x+1的個數,小于x+2x+2x+2的個數,小于x+kx+kx+k的個數把他們累加。
然后就死了???這不就是前綴和然后區間詢問嗎?wtcl
開個桶,然后做一個前綴和,然后就是個區間詢問[x,x+k][x,x+k][x,x+k]的問題。。。
此題首先記錄每個數出現的位置,單獨考慮每個數作為區間的絕對眾數。
不妨設當前考慮的數為v\text vv
v\text vv能作為區間(L,R](\text L,\text R](L,R]的眾數的充要條件是
cntR?cntL>R?L2\text{cnt}_{\text R}-\text{cnt}_{\text L}>\frac {\text R-\text L}{2}cntR??cntL?>2R?L?
即
2cntL?L<2cntR?R,L<R2\text{cnt}_{\text L}-\text L<2\text{cnt}_{\text R}-\text R ,\text{L}<\text R 2cntL??L<2cntR??R,L<R
記bL=2cntL?L\text b_{\text L}=2\text{cnt}_{\text L}-\text LbL?=2cntL??L
顯然我們枚舉右端點,用個樹狀數組就可以統計,但是復雜度不行。
觀察每個數出現的位置將整個區間劃分為下面模式
[1,…)[…)[…)[…,n+1)\color{blue}[1,\dots)[\dots)\color{red}[\dots)\color{black}[\dots,\text{n+1}) [1,…)[…)[…)[…,n+1)
上面)))代表的就是該數出現的位置。
對于每個[…)\color{red}[\dots)[…)內部bi\text b_{\text i}bi?是單調下降,且公差為1,顯然當區間右端點在此區間內部時,區間左端點不可能在其內部。換句話說就是只有前面的即[1,…)[…)\color{blue}[1,\dots)[\dots)[1,…)[…)可能對區間右端點在[…)\color{red}[\dots)[…)產生貢獻。
而且重要的是[…)\color{red}[\dots)[…)的bj\text b_{\text j}bj?連續的,即對于每一個[…)\color{red}[\dots)[…)的bj\text b_{\text j}bj?我們需要在[1,…)[…)\color{blue}[1,\dots)[\dots)[1,…)[…)找到bi<bj\text b_{\text i}<\text b_{\text j}bi?<bj?,顯然就是最開始那個問題,只需要求個前綴和然后區間詢問即可。
每次過考慮[…)\color{red}[\dots)[…)后進行區間修改,將[…)\color{red}[\dots)[…)內部的bj\text b_{\text j}bj?插入數據結構中。
區間修改+前綴和區間詢問
樹狀數組的話可以把差分轉化為單點修改,那么本次要做到區間詢問就意味著要用樹狀數組維護三維前綴和
∑i=1n∑j=1i∑k=1jdk=12[(n2+3n+2)∑i=1ndi?(2n+3)∑i=1ni?di+∑i=1ni2?di]\sum_{i=1}^{n}\sum_{j=1}^i\sum_{k=1}^jd_k=\frac{1}{2}[(n^2+3n+2)\sum_{i=1}^nd_i-(2n+3)\sum_{i=1}^ni·d_i+\sum_{i=1}^ni^2·d_i] i=1∑n?j=1∑i?k=1∑j?dk?=21?[(n2+3n+2)i=1∑n?di??(2n+3)i=1∑n?i?di?+i=1∑n?i2?di?]
要加油哦~
總結
以上是生活随笔為你收集整理的P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客练习赛10 F-序列查询(莫队+链表
- 下一篇: 数码相机的几倍变焦是什么意思