Codeforces Round #555 (Div. 3) D. N Problems During K Days 【数学思维】

时间:2023-03-10 02:37:27
Codeforces Round #555 (Div. 3) D. N Problems During K Days 【数学思维】

一 题面

  D. N Problems During K Days

二 分析

  对于这题,刚开始我就是陷入了对公式的执着,企图用公式直接确定第一个数,然后试着去找序列。经过思考和手动模拟后发现是很难保证正确性的。

  对于这题,第一步是很明确的,就是确定第一个数,如果给定了$N$和$K$,那么第一个数是可以确定的。因为如果我们只保证后一个数比当前数多1,并且确定第一个数是1,那么可以根据$K$确定对应的最小和$sum$

$sum = \frac{K(K+1)}{2}$

这个公式应该比较基础。这样我们就可以把$N<sum$的情况就全部排除掉了。

  接下来,分析确定的$K$和$N>=sum$的情况:

  首先,可以确定一个公式

$N - sum = {K}\times{t} + res$  $res<K$

这个公式也比较基础。

  有了这个公式后,就比较明了了,因为可以在$sum$的基础上把$t$个$K$平摊掉,就是对应的$K$个数每个数加1。

  接下来处理$res$,这里可能需要动脑袋思考一下。但我们依然需要明确,第一个数是不可以改变的,因为第一个数改变了会直接影响后面的所有数。

  处理$res$,我们可以直接从当前确定的数组从后往前进行加操作,操作的前提是满足题目给定的条件。加了之后会发现,如果当前的数加了一个数后,那么右边已经加过的数范围又变了,又可以加了,但是是有限度的,因为第一个数已经确定了。(这里也提醒了我们为什么不从左往右进行处理)

  处理$res$的时候,如果出现了遍历一遍后,$res > 0$并且没有加数的情况,就表示数组已经无法满足题目给定的条件了,直接输出$NO$即可。

三 AC代码

 #include <bits/stdc++.h>

 using namespace std;
#define LL long long
const int MAXN = 1e5 + ;
int N;
LL K;
int Data[MAXN]; int main()
{
ios::sync_with_stdio(false);
cin >> N >> K;
//第一个数为1的最基础的情况
LL sum = (K * (K + ) ) >> ;
if(sum > N)
{
cout << "NO" << endl;
return ;
}
N -= sum;
int res = N % K;
Data[] = + N/K;
for(int i = ; i <= K; i++)
Data[i] = Data[i - ] + ;
//满足最低要求后,还剩下res < k
while(res)
{
bool flag = true;
//第一个数一定不能变
for(int i = K; i > ; i--)
{
int tmp = (Data[i - ] << ) - Data[i];
if( res > && tmp > )
{
//数组中有数变化了
flag = false;
if(res >= tmp)
{
Data[i] += tmp;
res -= tmp;
}
else
{
Data[i] += res;
res = ;
}
}
}
if(flag)
{
cout << "NO" << endl;
return ;
}
}
cout << "YES" << endl;
for(int i = ; i < K; i++)
{
cout << Data[i] << " ";
}
cout << Data[K] << endl;
return ;
}