题解:AT_abc200_e [ABC200E] Patisserie ABC 2

目前暂无修正。

前言:终于轮到我复杂问题简单化啦哈哈哈。

为什么题解区一车容斥啊?复杂难推导且根本没必要。这里给出一个桶 + 前缀和的做法。与这篇题解类似,但是由于其并没有详细地写出过程,写得也较为简略,所以这里来补充并完善一下这个做法的本质。

形式化题意\(n^3\) 个三元组 \((x,y,z)\) 按照 \(x+y+z\) 为第一关键字,\(x\) 为第二关键字,\(y\) 为第三关键字排序并求第 \(k\) 个。

看到求第 \(k\) 排名,我们容易想到按照关键字依次确定。

先确定 \(x+y+z=A\),按照 \(A\) 升序扫一遍,扫到差不多 \(k\) 的位置停止并记录 \(A\),通过前缀和实时计算有多少个有序三元组 \((x,y,z)\) 满足 \(x+y+z\le A\) 的。再根据 \(A\) 从小到大扫一遍 \(x\),并根据前缀和实时计算有多少个有序二元组 \((y,z)\) 满足 \(y+z\geq A-x\) 的,最后 \(\Theta(n)\) 确定 \(y\) 即可。

我们需要先求出那么要求的东西就变成了:

  1. 满足 \(x+y+z=A\) 的有序三元组 \((x,y,z)\) 的数量。
  2. 满足 \(y+z=B\) 的有序二元组 \((y,z)\) 的数量。

\(buk_2[B]\) 表示第二条的答案,显然随便列个不等式分讨一下就可以 \(\Theta(n)\) 计算,再详细一点就是 \(y+z=B,y\in[1,n],z\in[1,n]\),读者可自行思考。

主要难点在于 \(buk_3[A]\) 如何求解。枚举多出来的一个数 \(x\),有转移:\(buk_3[A]=\sum_{x\in[1,n]}buk_2[A-x]\),然后由于 \(x\in[1,n]\),这个东西实际上是 \(buk_2\) 的一段连续区间,直接前缀和计算即可。总时间复杂度 \(\Theta(n)\)

感觉代码可读性挺高的。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=3e6+5;
LL n,k,sum,A,B,tot,x,y,z;
LL buk2[N],buk3[N];
int main(){
	scanf("%lld%lld",&n,&k);
	for(int i=2;i<=2*n;i++){
		if(i>n)buk2[i]=(n-(i-n)+1);
		else buk2[i]=i-1;
	}
	LL pre=0;
	for(int i=3;i<=3*n;i++){
		if(i>=(n+1))pre-=buk2[i-(n+1)];
		pre+=buk2[i-1];
		buk3[i]=pre;
	}
	for(sum=3;sum<=3*n;sum++){
		tot+=buk3[sum];
		if(tot>=k){tot-=buk3[sum];break;}
	}
	for(x=1;x<=n;x++){
		tot+=buk2[sum-x];
		if(tot>=k){tot-=buk2[sum-x];break;}
	}
	for(y=1;y<=n;y++){
		if(sum-x-y>n)continue;
		tot++;
		if(tot==k)break;
	}
	z=sum-x-y;
	printf("%lld %lld %lld",x,y,z);
	return 0;
}
posted @ 2025-10-28 15:12  TBSF_0207  阅读(6)  评论(0)    收藏  举报