[Ynoi2019 模拟赛] Yuno loves sqrt technology I

发布时间 2023-06-19 20:02:03作者: 云浅知处

题目 Link

分块,首先预处理所有整块之间的答案,这部分用类似莫队二离的手法可以改成 \(O(n)\) 次插入和 \(O(n\sqrt{n})\) 查询,然后根号平衡一手做到 \(O(n\sqrt{n})\);空间自然也是能线性的。当然更直白的说法是,直接预处理 \(f(i,j)\) 表示前 \(i\) 块中 \(>j\) 的元素个数。

然后考虑区间 \([l,r]\) 被分成 \(A_1,B,A_2\) 这样的左右散块和整块,那么我们已经处理好了 \(B\to B\),还剩下五块:\(A_1\to A_1/B/A_2,B\to A_2,A_2\to A_2\)\(A_1\to B,B\to A_2\) 都可以利用 \(f\) 查询出来。

考虑 \(A_1\to A_1\),发现只需要从后往前扫,预处理 \(S_i\) 表示 \(i\) 同块内后面比他小的数的个数,那么只需要查 \(S\) 的区间和。同理 \(A_2\to A_2\) 也可以通过预处理 \(P_i\) 表示同块内前面比他大的数的个数来解决。

最后考虑 \(A_1\to A_2\),考虑提前把每块排序,然后查询的时候尝试归并这两块即可。

还需要考虑同块的情形。考虑差分成 \(P_r\) 减去 \(P_{l-1}\) 再减去 \([L,l-1]\to [l,r]\) 的贡献,其中 \(L\) 是这一块的左端点。那么这个贡献用这一块的排序后的数组也可以轻松算出。

卡到了最优解!

//-DYUNQIAN -std=c++14 -O2 -Wall
#include<bits/stdc++.h>

#define ll long long

using namespace std;

inline ll read(){
	ll x=0;char c=getchar();
	for(;(c<'0'||c>'9');c=getchar());
	for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
	return x;
}

const int mod=1e9+7;
int ksm(int x,int y,int p=mod){
	int ans=1;
	for(int i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p;
	return ans%p;
}
int inv(int x,int p=mod){return ksm(x,p-2,p)%p;}
mt19937 rnd(time(0));
int randint(int l,int r){return rnd()%(r-l+1)+l;}

void Assert(bool c,int L=0){if(!c){cout<<"Assertion Failed at "<<L<<endl;exit(0);}}

const int N=1e5+5;
const int B=400;
const int NB=(int)(N/B);

vector<pair<int,int> >ov[NB+5];
#define fi first
#define se second
#define mk make_pair
int n,m,bl[N],st[NB+5],ed[NB+5],f[NB+5][N],pre[N],suf[N],a[N],C,S;
ll ans[NB+5][NB+5],fp[N],fs[N],g[NB+5][N];

struct BIT{
	int c[N];
	int lowbit(int x){return x&(-x);}
	void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i))c[i]+=v;}
	int sum(int x,int res=0){for(int i=x;i;i-=lowbit(i))res+=c[i];return res;}
}T;

ll Q1(int l,int r){
	if(l==st[bl[l]])return fp[r];
	else if(r==ed[bl[r]])return fs[l];
	int p=bl[l],cur=0;ll sum=fp[r]-fp[l-1];
	for(auto t:ov[p]){
		if(t.se<=l-1)cur++;
		else if(t.se>=l&&t.se<=r)sum-=cur;
	}
	return sum;
}
int Q_leq(int l,int r,int v){// st[l] <= j <= ed[r] && a[j] < v
	return f[r][v-1]-f[l-1][v-1];
}
int Q_geq(int l,int r,int v){return ed[r]-st[l]+1-f[r][v-1]+f[l-1][v-1];}
ll query(int l,int r){
	if(bl[l]==bl[r])return Q1(l,r);
	int pl=bl[l],pr=bl[r];
	ll sum=fp[r]+fs[l]+ans[pl+1][pr-1];//A1 -> A1 , A2 -> A2 , B -> B
	
	//A1 -> B & B -> A2 
	sum+=g[pr-1][ed[pl]]-g[pr-1][l-1],sum-=g[pl][ed[pl]]-g[pl][l-1];
	sum-=g[pr-1][r]-g[pr-1][st[pr]-1],sum+=g[pl][r]-g[pl][st[pr]-1];
	sum+=1ll*(ed[pr-1]-st[pl+1]+1)*(r-st[pr]+1);
	
	//A1 -> A2
	int L=ov[pl].size(),p=0,cur=0;
	for(auto t:ov[pr]){
		if(t.se>r)continue;
		while(p<L&&ov[pl][p].fi>t.fi)cur+=(ov[pl][p].se>=l),p++;
		sum+=cur;
	}
	
	return sum;
}

signed main(void){

#ifdef YUNQIAN
	freopen("5046.in","r",stdin);
	freopen("5046.out","w",stdout);
#endif

	n=read(),m=read();S=B;
	for(int i=1;i<=n;i++)a[i]=read();
	
	memset(st,63,sizeof(st));
	for(int i=1;i<=n;i++)bl[i]=(i-1)/S+1,st[bl[i]]=min(st[bl[i]],i),ed[bl[i]]=i;C=bl[n];
	for(int i=1;i<=C;i++){
		for(int j=st[i];j<=ed[i];j++)f[i][a[j]]++;
		for(int j=1;j<=n;j++)f[i][j]+=f[i][j-1];
		for(int j=1;j<=n;j++)f[i][j]+=f[i-1][j];
		
		int now=0;
		for(int j=st[i];j<=ed[i];j++)pre[j]=now-T.sum(a[j]),T.add(a[j],1),now++;
		for(int j=st[i];j<=ed[i];j++)T.add(a[j],-1);now=0;
		for(int j=ed[i];j>=st[i];j--)suf[j]=T.sum(a[j]),T.add(a[j],1);
		for(int j=st[i];j<=ed[i];j++)T.add(a[j],-1);

		for(int j=st[i]+1;j<=ed[i];j++)fp[j]=fp[j-1]+pre[j];
		for(int j=ed[i]-1;j>=st[i];j--)fs[j]=fs[j+1]+suf[j];
		
		for(int j=st[i];j<=ed[i];j++)ov[i].emplace_back(mk(a[j],j));
		sort(ov[i].begin(),ov[i].end());reverse(ov[i].begin(),ov[i].end());
	}
	
	for(int i=1;i<=C;i++){
		int cur=0;
		for(int j=i;j<=C;j++){
			ans[i][j]=ans[i][j-1];
			for(int k=st[j];k<=ed[j];k++)ans[i][j]+=pre[k]+cur-f[j-1][a[k]]+f[i-1][a[k]];
			cur+=ed[j]-st[j]+1;
		}
	}
	
	//g[i][j] = sum{k in [1,j]} f[i][a[k]]
	for(int i=1;i<=C;i++){
		for(int j=1;j<=n;j++)g[i][j]=g[i][j-1]+f[i][a[j]];
	}
	
//	cout<<"prework Time = "<<(clock()-Start)/CLOCKS_PER_SEC<<endl;
	
	ll lans=0;
	for(int i=1;i<=m;i++){
// 		lans=0;
		ll l=read(),r=read();l^=lans,r^=lans;
		cout<<(lans=query(l,r))<<'\n';
	}

	return 0;
}