Atcoder Beginner Contest 200 E - Patisserie ABC 2 题解
当想了半个多小时三维坐标系的菜比看到了组合数学题解
题意
将 \(1\leq i, j, k\leq N\) 的 \(N^3\) 个三元组 \((i, j, k)\) 进行排序,排序时以总和为第一关键字,第一维为第二关键字,第二维为第三关键字,从小到大排。
询问第 \(K\) 个三元组
分析
如果用三维坐标系描述,不难想到,三元组分布在长宽高均为 \(N\) 的正方体上;将其按 \(x+y+z=C\) 进行切片,就能得到第一关键字;同理分析得到第二第三。
如果具有极其恐怖的空间想象能力,可以试试这个方法。反正我是不行
考虑 \(i+j+k=S\) 的方案数:
等效于 \(S\) 个小球塞入 \(3\) 个盒子,每个盒子非空且容积为 \(n\) ,问方案数。
不考虑 \(n\) 的限制,则方案数用隔板法得 \(\dbinom{S-1}{2}\) 。
现考虑有一个数超过 \(n\) 的限制,我们对它容斥:先分配给这个数 \(n\) ,再用隔板法分配得 \(\dbinom{S-n-1}{2}\);超过 \(n\) 的那个数有 \(\dbinom{3}{1}\) 种选法。
同理容斥两个数、三个数超过 \(n\) 限制的情况,得到最后方案数为:
\(\displaystyle \sum_{i=0}^3(-1)^i\dbinom{3}{i}\cdot \dbinom{S-i\cdot n-1}{2}\) 。
由于组合数下方的数字都较小,直接暴力展开可求,复杂度 \(O(1)\) 。
因为层数最高为 \(3n\) ,直接暴力枚举过去,找到在第几层即可,复杂度为 \(O(n)\) 。
求出第一关键字后,我们得到了 \(i+j+k=S\) 的答案,然后考虑求 \(i\) 。
我们直接从 \(1\) 到 \(n\) 枚举 \(i\) ,则此时 \(j+k=S-i\) 的方案数:
等效于 \(S-i\) 个小球塞入 \(2\) 个盒子,每个盒子非空且容积为 \(n\) ,问方案数。
同上得到为: \(\displaystyle \sum_{j=0}^2(-1)^j\dbinom{2}{j}\cdot \dbinom{S-i-j\cdot n-1}{1}\) 。
同理直接展开,然后暴力枚举,复杂度为 \(O(n)\) 即可确定 \(i\) 。
确定 \(i\) 与 \(S\) 后,我们直到 \(j+k=S-i\) ,则 \(j_{\min}=\max(S-i-n, 1)\) 。
当我们筛除上面的方案数,确定 \((i, j, k)\) 为 \(j+k=S-i\) 的第 \(K\) 个后,则 \(j_K=j_{\min}+K-1\) 。
然后用 \(k=S-i-j_K\) 求出 \(k\) 输出即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=3e6+10;
ll n, k, sum[MAXN];
inline ll calc3(ll k){
ll res=(k-1)*(k-2)/2;
k-=n;
if(k<=0) return res;
res-=(k-1)*(k-2)/2*3;
k-=n;
if(k<=0) return res;
res+=(k-1)*(k-2)/2*3;
k-=n;
if(k<=0) return res;
return res-(k-1)*(k-2)/2;
}
inline ll calc2(ll k){
ll res=k-1;
k-=n;
if(k<=0) return res;
res-=(k-1)*2;
k-=n;
if(k<=0) return res;
return res+(k-1);
}
inline void work(){
ll flr=3;
while(sum[flr]<k)
k-=sum[flr++];
for(int i=1;i<=n;++i){
ll res=calc2(flr-i);
if(res<k){
k-=res;
continue;
}
ll x=max(flr-i-n, 1ll)+(k-1);
cout<<i<<" "<<x<<" "<<flr-i-x;
break;
}
}
inline void pre(){
cin>>n>>k;
for(int i=1;i<=3*n;++i) sum[i]=calc3(i);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
pre();
work();
cout.flush();
return 0;
}