[ABC200-E]Patisserie ABC 2

壹、题目大意 ¶

传送门 to Atcoder.

你有 \(n^3\) 个三元组 \((i,j,k)\),这些三元组各不相同,且 \(i,j,k\) 都是 \([1,n]\) 之间的正整数。

你现在以 \(i+j+k\) 为第一关键字,\(i\) 为第二关键字,\(j\) 为第三关键字将所有的三元组从小到大排序。

请问第 \(K\) 个三元组,它的 \(i,j,k\) 分别是多少。

贰、题解 ¶

不难发现,我们只需要快速地找到 \(i+j+k\) 的和为多少,然后枚举 \(i\),就可以直接算出 \(j,k\) 了。

所以,这道题地难点,或者说关键是找到 \(s=i+j+k\),对于找到 \(s\),这里有许多做法

§ 朴素算法 \(\mathcal O(n)\) §

我们可以通过背包求出和为每个数的三元组的个数。

然后枚举第 \(K\) 个三元组的和是多少,这个枚举过程也可以先将背包求前缀和,再用二分实现,但由于要求前缀和,时间复杂度并不会变得优秀。

之后再通过相似的枚举得到 \(i\) 的值与 \(j\) 的值,就可以求出 \(k\) 了。

时间复杂度 \(O\left(n\right)\).

§ 优化壹:没有 DP!§

我们能否使用数学方法将 \(s\) 找到?比较快速地?比如说 \(\mathcal O(1)\)

考虑生成函数:令 \(f(x)=\sum_{i=0}^\infty x^i\),定义 \(g(x)\) 表示方案数的生成函数,根据定义有 \([x^j]g(x)\) 为三个数的和为 \(j\) 的方案数。

考虑使用 \(f(x)\) 表示出 \(g(x)\),显然有

\[g=(f-x^{n+1}f-1)^3 \]

这是为什么?先考虑一个数,首先有最基础的无限级数求和 \(f(x)\),但是一个数不能超过 \(n\),所以减去 \(x^{n+1}f(x)\),并且还有常数项 \(-1\),有三个数,所以对于 \((f(x)-x^{n+1}f(x)-1)\) 的三次方,最后就得到了 \(g(x)\).

考虑一个很经典的收敛,\(f(x)=\frac{1}{1-x}\),我们不难得到 \(g(x)\) 的闭形式:

\[g(x)=(\frac{1-x^{n+1}}{1-x}-1)^3=\frac{(x-x^{n+1})^3}{(1-x)^3} \]

考察 \(g(x)\)\(x=0\) 处的泰勒展开:

\[g(x)=((-(x^n)^3)+3(x^n)^2-3x^n+1)\times x^3 \\ +((-3(x^n)^3)+9(x^n)^2-9x^n+3)\times x^4 \\ +((-6(x^n)^3)+18(x^n)^2-18x^n+6)\times x^5 \\ +((-10(x^n)^3)+30(x^n)^2-30x^n+10)\times x^6 \\ +((-15(x^n)^3)+45(x^n)^2-45x^n+15)\times x^7 \\ +((-21(x^n)^3)+63(x^n)^2-63x^n+21)\times x^8 \\ +((-28(x^n)^3)+84(x^n)^2-84x^n+28)\times x^9 \\ +... \]

发现对于 \(x^3,x^4,x^5\) 的系数是 \(\{1,3,6,10,15,21,28,...\}\) 是等差数列求和公式 \(S(n)={n(n+1)\over 2}\) 的数列,那么

\[[x^i]g(x)=S(i-2)-3\times S(i-n-2)+3\times S(i-2n-2) \]

其中,\(n\) 是一个常数。

这样,我们就可以摆脱使用 \(dp\) 推出方案数,而是可以直接算了。

对于为什么 \(g(x)\) 有上面的式子,我们可以使用容斥对这个结论进行说明:

如果我们要求 \(i+j+k=sum\) 的情况, 其中满足 \(1\le i,j,k\le n\).

首先,我们先把 \(i,j,k\le n\) 的限制扔掉,那么就有 \({sum-1\choose 2}\) 种方案(插板法),然后减去有 \(1/2/3\) 个数字超过 \(n\) 的情况:

\(3\) 个数都大于 \(n\) 的情况,我们可以强制给每个数都塞 \(n\),剩下的 \(sum-3n\) 也使用插板法解决,有 \(sum-3n-1\choose 2\) 种方案。

然后,我们钦定一个数超过 \(n\),显然有三个这样的数字,所以方案数减去 \(sum-2n-1\choose 2\),但是对于两个数大于 \(n\) 的情况我们多减去了三次,所以还要加回来,最后的方案数就是

\[{sum-1\choose 2}-{sum-3n-1\choose 2}-3{sum-n-1\choose 2}+3{sum-2n-1\choose 2} \]

化简之后就是上面的东西了。

§ 优化贰:\(\mathcal O(1)\) §

如果得到了前缀和,那么我们就可以使用二分了。

考虑再对 \([x^i]g(x)\) 求关于 \(i\) 的前缀和,设 \([x^i]g(x)\) 的生成函数为 \(\sigma\),则有

\[\sigma=Sx^2-3\times S^{n+2}+3\times S^{2n+2} \]

考虑将 \(\sigma\) 与单位向量 \(I\) 卷起来:

\[I\cdot \sigma =\frac{1}{1-x}(Sx^2-3S^{n+2}+3S^{2n+2}) \]

\(\sigma\cdot I\) 进行麦克劳林展开:

\[ (I\cdot \sigma)(x)=(-x^{3n}+3x^{2n}-3x^n+1)x^3+(-4x^{3n}+12x^{2n}-12x^n+4)*x^4+... \]

定义 \(\alpha(x)=x(x+1)(x+2)/6\),那么 \(I\cdot \sigma\) 的通项公式即为 \(\alpha(x-2)-3\alpha(x-n-2)+3\alpha(x-2n-2)\)

这个东西在 \((0,+\infty)\) 单增,那么我们可以考虑不等式,即有如下不等式:

\[\alpha(x-2)-3\alpha(x-n-2)+3\alpha(x-2n-2)\le K \\ \Rightarrow {{x^3+\left(-9\,n-3\right)\,x^2+\left(27\,n^2+18\,n+2\right)\,x-21 \,n^3-27\,n^2-6\,n}\over{6}}-K\leqslant 0 \\ \Rightarrow x=\left({{\sqrt{ 243\,n^6-486\,K\,n^3+243\,K^2-1}}\over{3^{{{3}\over{2}}}}}+{{\left(- 9\,n-3\right)\,\left(27\,n^2+18\,n+2\right)-3\,\left(-6\,K-21\,n^3- 27\,n^2-6\,n\right)}\over{6}}+{{\left(-1\right)\,\left(-9\,n-3 \right)^3}\over{27}}\right)^{{{1}\over{3}}} \\ -{{{{\left(-1\right)\, \left(-9\,n-3\right)^2}\over{9}}+{{27\,n^2+18\,n+2}\over{3}}}\over{ \left({{\sqrt{243\,n^6-486\,K\,n^3+243\,K^2-1}}\over{3^{{{3}\over{2 }}}}}+{{\left(-9\,n-3\right)\,\left(27\,n^2+18\,n+2\right)-3\,\left( -6\,K-21\,n^3-27\,n^2-6\,n\right)}\over{6}}+{{\left(-1\right)\, \left(-9\,n-3\right)^3}\over{27}}\right)^{{{1}\over{3}}}}}+3n+1 \]

我们真的没有使用在线计算器:)

对于后面的 \(i,j\) 也可以使用这种简单的不等式在 \(\mathcal O(1)\) 内推导。

所以总时间复杂度为 \(\mathcal O(1)\),代码实现 \(\mathcal O(+\infty)\).

叁、参考代码 ¶

只有背包的代码咯:

for(LL i=1;i<=n;i++)dp[1][i]=1LL,dp[1][i]+=dp[1][i-1];
for(LL i=n+1;i<=3*n;i++)dp[1][i]=dp[1][n];
for(LL i=2;i<=2*n;i++)dp[2][i]+=dp[1][i-1]-dp[1][max(0LL,i-n-1LL)],dp[2][i]+=dp[2][i-1];
for(LL i=2*n+1;i<=3*n;i++)dp[2][i]=dp[2][2*n];
for(LL i=3;i<=3*n;i++)dp[3][i]+=dp[2][i-1]-dp[2][max(0LL,i-n-1LL)];
for(LL i=3;i<=3*n;i++){
	if(k<=dp[3][i]){
		for(LL j=1;j<=n;j++){
			if(i-j>2*n)continue;
			LL down=max(1LL,i-j-n),up=min(n,i-j-1);
			if(k>up-down+1LL)k-=(up-down+1LL);
			else{
				printf("%lld %lld %lld\n",j,down+k-1LL,i-j-down-k+1LL);
				return 0;
			}
		}
	}
	else k-=dp[3][i];
}
posted @ 2021-05-10 20:55  Arextre  阅读(261)  评论(0编辑  收藏  举报