[TK] 颜色

谴责这道题发明在线莫队的人,简直就是异端,还好我给在线莫队卡了,支持正义制裁

题意简述

给定序列,设 \(f(l,r,x)\) 表示 \(x\)\([l,r]\) 内的出现次数,对给定 \(l,r,a,b\)\(\sum^{i}_{a\le i\le b} f^{2}(l,r,i)\). (强制在线)

解题思路

既然强制在线了,用不了莫队,那么为什么不用分块做呢.

首先不想分块,先想这题怎么求. 如果我们要求 \(\sum^{i}_{a\le i\le b} f^{2}(l,r,i)\), 我们可以用 \(\sum^{i}_{1\le i\le b} f^{2}(l,r,i) - \sum^{i}_{1\le i\le a-1} f^{2}(l,r,i)\). 这里用的是前缀和的思想,也是很显然的. 那么我们考虑就这样维护一个 \(ans[l][r][k]\) 数组来表示 \(\sum^{i}_{1\le i\le k} f^{2}(l,r,i)\),这样在问的时候不就能直接求了吗.

不过我们这样做有一个小问题,就是三维数组太大,而序列长度也大,会炸,只能处理范围小一点的数据. 所以这个时候分块登场了. 我们可以利用这样的思想拆开区间,来求解整块的数据,剩下的打暴力,最后再把全部子区间答案加一下就好了.

但是两边打暴力也挺慢的,因为我们有不少元素需要处理,会显得效率很低. 所以我们考虑怎么快速计算 \(f(l,r,x)\). 再用一遍前缀和思路,我们利用 \(f(l,r,x)=f(1,r,x)-f(1,l-1,x)\),维护一个数组 \(sum[i][j]\),表示在块 \([1,j]\) 内有多少个 \(i\),这样我们的非整块部分也高效优雅了起来.

接下来考虑怎么在预处理部分把 \(ans[l][r][k]\)\(sum[i][j]\) 全部算出来.

对于 \(ans\),按我们从左向右遍历的思路,很容易发现 \(ans[l][r][k]=ans[l][r-1][k]+f(r,r,k)\). 即为前 \(r-1\) 个块内的答案加上第 \(r\) 块内的答案. 很容易,但注意我们这里的加不是单纯的加. 因为我们定义的时候用的是平方和. 假如我们之前有 \(x\) 个,现在要加上 \(1\) 个,那么应该加上 \((x+1)^{2}-x^{2}=2x+1\). 这是一个需要注意的点.

对于 \(sum\),也很容易发现 \(sum[i][j]=sum[i-1][j]+ f(area\ i)\). 那么我们就可以拿下这道题了.

代码实现

#include<bits/stdc++.h>
using namespace std;
int a[50001],sum[55][20001],//hou many j in fore i area
ans[55][55][20001],//how many [1,k] ans (sqre) from i to j
n,m,q,len,
l[50001],r[50001],
locate[50001],
tot[20001];
void prework(){
	len=pow(n,0.666);
	l[1]=1;
	//work out the location
	for(int i=1;i<=n;++i){
		locate[i]=i/len+(i%len!=0);
		if(i%len==0){
			l[locate[i]+1]=i+1;
			r[locate[i]]=i;
		}
	}
	//work out the sum : sum[i][j]=sum[i-1][j]+ amount of area i.
	for(int i=1;i<=locate[n];++i){
		for(int j=1;j<=m;++j){
			sum[i][j]=sum[i-1][j];
		}
		for(int j=l[i];j<=r[i];++j){
			sum[i][a[j]]++;
		}
	}
	//work out the ans : ans[i][j][k]=ans[i][j-1][k]+the number of amount k of area j.
	//because ans is after sqre, so when add a number, ans from x^2 to (x+1)^2, which need to add 2x+1.
	//so tot[] actually store amount of k before each add.
	for(int i=1;i<=locate[n];++i){
		for(int j=i;j<=locate[n];++j){
			for(int k=1;k<=m;++k){
				ans[i][j][k]=ans[i][j-1][k];
			}
			for(int k=l[j];k<=r[j];++k){
				ans[i][j][a[k]]+=tot[a[k]]*2+1;
				tot[a[k]]++;
			}
		}
		for(int j=l[i];j<=n;++j){
			tot[a[j]]&=0;
		}
	}
	for(int i=1;i<=locate[n];++i){
		for(int j=i;j<=locate[n];++j){
			for(int k=1;k<=m;++k){
				ans[i][j][k]+=ans[i][j][k-1];
			}
		}
	}
}
int ask(int nl,int nr,int na,int nb){
	//first solve whole area, using ans we worked out : [na,nb] = [1,nb]-[1,na-1]
	int res=ans[locate[nl]+1][locate[nr]-1][nb]-ans[locate[nl]+1][locate[nr]-1][na-1];
	//work out the other part, the same as we work out the ans
	//1.when nl,nr in same area (avoid adding a area two times)
	if(locate[nl]==locate[nr]){
		for(int i=nl;i<=nr;++i){
			if(a[i]>=na&&a[i]<=nb){
				res+=tot[a[i]]*2+1;
				tot[a[i]]++;
			}
		}
		for(int i=nl;i<=nr;++i){
			tot[a[i]]=0;
		}
		return res;
	}
	//2.else : left and right
	for(int i=nl;i<=r[locate[nl]];++i){
		if(a[i]<na||a[i]>nb){
			continue;
		}
		if(!tot[a[i]]){
			tot[a[i]]=sum[locate[nr]-1][a[i]]-sum[locate[nl]][a[i]];
		}
		res+=tot[a[i]]*2+1;
		tot[a[i]]++;
	}
	for(int i=l[locate[nr]];i<=nr;++i){
		if(a[i]<na||a[i]>nb){
			continue;
		}
		if(!tot[a[i]]){
			tot[a[i]]=sum[locate[nr]-1][a[i]]-sum[locate[nl]][a[i]];
		}
		res+=tot[a[i]]*2+1;
		tot[a[i]]++;
	}
	//clear tot[]
	for(int i=nl;i<=r[locate[nl]];++i){
		tot[a[i]]&=0;
	}
	for(int i=l[locate[nr]];i<=nr;++i){
		tot[a[i]]&=0;
	}
	return res;
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	prework();
	int o,p,y,u,last=0;
	while(q --> 0){
		scanf("%d%d%d%d",&o,&p,&y,&u);
		o^=last;
		p^=last;
		y^=last;
		u^=last;
		last=ask(o,p,y,u);
		printf("%d\n",last);
	}
}
posted @ 2024-04-22 16:38  HaneDaniko  阅读(31)  评论(6编辑  收藏  举报