日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)

發布時間:2023/12/3 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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}vlmid 數字v\text vv出現的次數。
cntvr\text{cnt}_{\text {vr}}cntvr?mid+1→vr\text{mid}+1\to \text {vr}mid+1vr 數字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}a1n?中求小于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=1n?j=1i?k=1j?dk?=21?[(n2+3n+2)i=1n?di??(2n+3)i=1n?i?di?+i=1n?i2?di?]

#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; vector<int> loc[N]; ll c0[N<<1],c1[N<<1],c2[N<<1]; void update(int k,ll v,int n) {ll i=k*v,ii=1ll*k*k*v;while(k<=n){c0[k]+=v;c1[k]+=i;c2[k]+=ii;k+=k&-k;} } ll qsum(int k) {ll ans=0;ll k1=1ll*k*k+3*k+2,k2=2*k+3;while(k){ans+=k1*c0[k]-k2*c1[k]+c2[k];k-=k&-k;}return ans>>1; } int main() {n=rd();rd();for(int i=1;i<=n;i++) a[i]=rd(),loc[a[i]].push_back(i);ll ans=0;const int Bs=n+1;for(int i=0;i<n;i++){if(loc[i].empty()) continue;int pre=0;loc[i].push_back(n+1);for(int j=0;j<loc[i].size();j++){int R=2*j-pre+Bs,L=2*j-(loc[i][j]-1)+Bs;ans+=qsum(R-1)-qsum(max(0,L-2));// 嚴格小于update(L,1,n<<1|1);update(R+1,-1,n<<1|1);pre=loc[i][j];}pre=0;for(int j=0;j<loc[i].size();j++){int R=2*j-pre+Bs,L=2*j-(loc[i][j]-1)+Bs;update(L,-1,n<<1|1);update(R+1,+1,n<<1|1);pre=loc[i][j];}}printf("%lld\n",ans);return 0; }

要加油哦~

總結

以上是生活随笔為你收集整理的P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。