ABC318E Sandwiches

ABC318E Sandwiches

第一次场切 E 题,感动。虽然比较水


注意到 \(\{a_n\}\) 的值域上限为 \(n\),考虑值域相关算法,对每一个 \(a_i\) 开一个 std::vector ,记作 \(pos_{a_i}\),存储 \(a_i\) 所有的出现位置。

枚举 \(x \in [1,n]\),遍历 \(pos_x\),当遍历到第 \(i\) 个时,枚举 \(j \in [1,i)\),此时需要统计二元组 \((j,i)\) 的贡献,即有多少个 \(k\in (i,j)\) 使得三元组 \((j,k,i)\) 符合要求。

容易发现这个贡献就是 \((pos_{x,i}-pos_{x,j}-1)-(i-j-1)\)\((pos_{x,i}-pos_{x,j}-1)\) 表示区间 \((i,j)\) 内有多少数,原因显然。\((i-j-1)\) 表示区间 \((i,j)\) 内有多少 \(a_i\),因为 \(pos_x\) 只存了 \(x\) 的出现位置,所以 \(\forall k \in (i,j),a_{pos_{x,k}}=a_i=a_j\)

至此有了一个总复杂度 \(\mathcal{O}(n^2)\) 的做法,代码:

const int N=3e5+8;
int n,a[N];
vector<int> pos[N];
bool Mend;
signed main(){
//	File_Work();
	fprintf(stderr,"%.3lf MB\n\n\n",(&Mbegin-&Mend)/1048576.0);
	n=read();
	for(int i=1;i<=n;i++)
		pos[i].push_back(0);
	for(int i=1;i<=n;i++){
		a[i]=read();
		pos[a[i]].push_back(i);
	}
	int ans=0;
	for(int x=1;x<=n;x++){
		if((int)pos[x].size()==1)
			continue;
		for(int i=1;i<(int)pos[x].size();i++)
			for(int j=1;j<i;j++)
				ans+=(pos[x][i]-pos[x][j]-1-(i-j-1));
	}
	write(ans);
	fprintf(stderr,"\n\n\n%.0lf ms",1e3*clock()/CLOCKS_PER_SEC);
	return 0;
}

考虑优化,容易发现瓶颈是枚举 \(j \in [1,i)\)

化简式子,二元组 \((j,i)\) 的贡献为 \(pos_{x,i}-pos_{x,j}-i+j\)

考虑 拆贡献\(pos_{x,i}\) 对答案产生了 \(i-1\) 次的正贡献,而 \(i\) 对答案产生了 \(i-1\) 次的负贡献。

对于与 \(j\) 有关的项,它们是以前缀和的方式贡献答案。具体地,每枚举到一个 \(i\)\(pos_{x,j}\) 对答案产生 \(\sum_{j=1}^{i-1}sum_{pos,j}\) 的负贡献,而 \(j\) 对答案产生 \(\sum_{j=1}^{i-1}j=\dfrac{i \times (i-1)}{2}\) 的正贡献。

因此预处理 \(pos_x\) 的前缀和,可以 \(\mathcal{O}(1)\) 计算出每个 \(i\) 对答案的贡献,故总复杂度 \(\mathcal{O}(n)\)

代码:

const int N=3e5+8;
int n,a[N];
vector<int> pos[N];
int sum[N];
bool Mend;
signed main(){
//	File_Work();
	fprintf(stderr,"%.3lf MB\n\n\n",(&Mbegin-&Mend)/1048576.0);
	n=read();
	for(int i=1;i<=n;i++)
		pos[i].push_back(0);
	for(int i=1;i<=n;i++){
		a[i]=read();
		pos[a[i]].push_back(i);
	}
	int ans=0;
	for(int x=1;x<=n;x++){
		if((int)pos[x].size()==1)
			continue;
		int sz=(int)pos[x].size();
		for(int i=1;i<sz;i++)
			sum[i]=sum[i-1]+pos[x][i];
		for(int i=1;i<sz;i++)
			ans+=pos[x][i]*(i-1)-sum[i-1]-i*(i-1)+i*(i-1)/2;
	}
	write(ans);
	fprintf(stderr,"\n\n\n%.0lf ms",1e3*clock()/CLOCKS_PER_SEC);
	return 0;
}
posted @ 2023-10-15 16:55  Irotan  阅读(8)  评论(0编辑  收藏  举报