目前暂无修正。
前言:终于轮到我复杂问题简单化啦哈哈哈。
为什么题解区一车容斥啊?复杂难推导且根本没必要。这里给出一个桶 + 前缀和的做法。与这篇题解类似,但是由于其并没有详细地写出过程,写得也较为简略,所以这里来补充并完善一下这个做法的本质。
形式化题意:\(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)\) 满足 \(A-(y+z)\le x\) 的,最后 \(\Theta(n)\) 确定 \(y\) 即可。
我们需要先求出那么要求的东西就变成了:
- 满足 \(x+y+z=A\) 的有序三元组 \((x,y,z)\) 的数量。
- 满足 \(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;
}