标签:ABC ABC200E int tt Patisserie freopen Put return dp
前言
你说为什么一定要想 \(O(n\log_2n)\) 或者数学做法直接算呢?OI 血统的 \(\tt dp\) 不香吗?
题目
讲解
我们如果知道了三种值的和,以及其对应的前缀方案数,那么就可以很轻松求出答案了。
考虑 \(\tt dp\)。这是一个类似于背包的过程,虽然跟背包好像差的有点远。
令 \(dp_{i,j}\) 表示 \(i\) 个数总和为 \(j\),每个数不超过 \(n\) 的方案数。其实知道了用 \(\tt dp\) 以及状态之后,转移并不困难,具体可以参见代码。
注意我们求的是前缀和形式。
时间复杂度 \(O(n)\)。
代码
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); k = Read();
for(int i = 1;i <= n;++ i) dp[0][i] = i;//这个是前缀和
for(int i = 2;i <= 2*n;++ i) dp[1][i] = dp[0][Min(i-1ll,n)] - dp[0][Max(i-n-1,0ll)],dp[1][i] += dp[1][i-1];
for(int i = 3;i <= 3*n;++ i) dp[2][i] = dp[1][Min(i-1ll,2*n)] - dp[1][Max(i-n-1,0ll)],dp[2][i] += dp[2][i-1];
for(int S = 3;S <= 3*n;++ S)
{
if(dp[2][S] >= k)
{
k -= dp[2][S-1];
for(int i = 1;i <= n;++ i)
if(S-i >= 2 && S-i <= 2*n)
{
if(dp[1][S-i]-dp[1][S-i-1] >= k)
{
for(int j = 1;j <= n;++ j)
if(S-i-j >= 1 && S-i-j <= n)
{
if(dp[0][S-i-j]-dp[0][S-i-j-1] >= k)
{
Put(i,' '),Put(j,' '),Put(S-i-j);
return 0;
}
else k -= dp[0][S-i-j]-dp[0][S-i-j-1];
}
}
else k -= dp[1][S-i] - dp[1][S-i-1];
}
}
}
return 0;
}
标签:ABC,ABC200E,int,tt,Patisserie,freopen,Put,return,dp 来源: https://www.cnblogs.com/PPLPPL/p/14761781.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。