1. 程式人生 > 其它 >題解[P5048 Yuno loves sqrt technology III]

題解[P5048 Yuno loves sqrt technology III]

題目連結
做此題前建議先做這題
雖然同樣是求區間眾數,但那題資料範圍小得多,空間限制也較鬆,這題還得另闢蹊徑。
考慮像那題一樣分塊,初始時用 \(O(n^{\frac{3}{2}})\) 的時間處理出兩兩塊中眾數的出現次數。
而由於空間限制不能處理出每個數在每個塊之前出現總次數,以處理散塊。
但事實上我們並不需要得知每個值真正的出現次數,只要看它能不能在區間中出現次數比當前答案大。
下以左散塊為例說明具體操作:
首先從左散塊的右端開始向 \(l\) 移動,設當前位置上的值為 \(x\) ,已得答案為 \(ans\)
那當前點的值能更新答案,就必須在區間內 \(x\) 之後 \(x\) 的個數比 \(ans\)

大。
於是就可以把原序列離散化,然後對每個值存入這些值所在序列中位置,可以用指標實現,常數小。
同時記錄序列中每個位置 \(i\) 上的值 \(x\) 之前 \(x\) 出現過的次數 \(rev_i\) (包括這個位置)。
\(pos_{x,i}\) 為第 \(i\) 個值為 \(x\) 的數的位置,\(cnt_x\) 為值為 \(x\) 的數的總出現次數。
那麼當前值能更新答案的條件是 \(rev_i+ans\leq cnt_x\) 並且 \(pos_{x,rev_i+ans}\leq y\)
滿足條件時 \(ans\) 便可加一。
由於是從左散塊的右端開始向 \(l\) 移動,擴充套件範圍逐漸變大,能保證正確性(略有不同與其他題解,但常數更小)
而對於右散塊同樣從其左端點開始向 \(r\)
移,能更新的條件便是:
\(rev_i > ans\) 並且 \(pos_{x,rev_i-ans}\geq l\)
同樣由於計算區間是逐漸擴大,正確性依然能保證。
時間複雜度: \(O(n^{\frac{3}{2}})\) 空間複雜度:\(O(n)\)
程式碼:(這樣做常數十分小,一點也不卡常) 若不是我強制線上寫錯好幾次就能一遍過了

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10,NN=710;
int n,m,x,y,nn,_n,sn,ans,last;char ch;
int a[N],b[N],id[N],l[N],r[N],res[NN][NN],cnt[N],tmp[N],rev[N],*pos[N];
inline void read(int &x){
	x=0;ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
inline void write(int x){if(x>=10)write(x/10);putchar('0'+x%10);}
inline void inquiry(int x,int y){
	ans=res[id[x]+1][id[y]-1];register int i;
	if(id[x]==id[y]){
		for(i=x;i<=y;++i)ans=max(ans,++tmp[a[i]]);
		for(i=x;i<=y;++i)--tmp[a[i]];
	}
	else {
		for(i=r[id[x]];i>=x;--i)if(rev[i]+ans<=cnt[a[i]])if(pos[a[i]][rev[i]+ans]<=y)++ans;
		for(i=l[id[y]];i<=y;++i)if(rev[i]>ans)if(pos[a[i]][rev[i]-ans]>=x)++ans;
	}
}
main(){
	read(n),read(m);nn=sqrt(n);sn=(n-1)/nn+1;register int i,j,k;
	for(i=1;i<=n;++i)read(a[i]),b[i]=a[i],id[i]=(i-1)/nn+1;
	for(i=1;i<=n;++i)r[id[i]]=i,l[id[n-i+1]]=n-i+1;
	sort(b+1,b+n+1);_n=unique(b+1,b+n+1)-b-1;
	for(i=1;i<=n;++i)a[i]=lower_bound(b+1,b+_n+1,a[i])-b,++cnt[a[i]];
	for(i=1;i<=_n;++i)pos[i]=new int [cnt[i]+1];
	for(i=1;i<=n;++i)pos[a[i]][++tmp[a[i]]]=i,rev[i]=tmp[a[i]];
	for(i=1;i<=sn;++i){
		for(j=1;j<=_n;++j)tmp[j]=0;
		for(j=i;j<=sn;++j){
			res[i][j]=res[i][j-1];
			for(k=l[j];k<=r[j];++k)res[i][j]=max(res[i][j],++tmp[a[k]]);
		}
	}
	for(j=1;j<=_n;++j)tmp[j]=0;
	while(m--){
		read(x),read(y);x^=last,y^=last;
		inquiry(x,y);write(last=ans);putchar('\n');
	}
}