CF1928E Modular Sequence 题解
考虑合法的答案的构成为一个 \(x,x+y,\dots x+ky\) 的块加上若干个 \(x\bmod y,x\bmod y+y,\dots x\bmod y+ky\) 的块。因为无论加多少次 \(y\),对 \(y\) 进行一次取模就都被消去了。
我们考虑枚举 \(x,x+y,\dots x+ky\) 的块的长度,然后判断剩余的数的总和能否被剩余的位置拼出。这里不判断剩余位置能否拼出剩余的数的总和,因为这个很难预处理,也很难快速计算。
我们把除了第一个块的元素,每一个都减去 \(x\bmod y\),再除以 \(y\),就变成了若干个 \(0,1,\dots k\) 的块。这样的好处是,如果拼出剩余的数的总和的序列长度小于剩余的位置,就可以补入 \(0\) 来占位。
我们记 \(dp[i]\) 为拼出总和为 \(i\) 的序列的最小长度。我们枚举 \(j\),表示新增一个长度为 \(j-1\) 的块,可以使用等差数列求和公式得到这个块的和,容易得到如下转移方程:
\[dp[i]=\min(dp[i],dp[i-\frac{j\times(j-1)}{2}]+j)(\frac{j\times(j-1)}{2}\le i)
\]
由于题目需要输出方案,所以还需要记录每次转移的前驱与新增块的长度,方便输出方案。
时间复杂度为 \(O(s\sqrt{s}+n)\),可以通过。
需要注意,有很多种情况会导致当前枚举的长度不可行,具体可以看代码。
#include <bits/stdc++.h>
using namespace std;
long long t,n,x,y,s,f[300000],pr[300000],pz[300000],a[300000];
int main()
{
scanf("%lld",&t);
while(t--)
{
bool flag=1;
scanf("%lld%lld%lld%lld",&n,&x,&y,&s);
for(int i=0;i<=s;i++)f[i]=1e10,pr[i]=0,pz[i]=0;
f[0]=0;
for(int i=1;i<=s;i++)
for(int j=2;j*(j-1)/2<=i;j++)
if(f[i-j*(j-1)/2]+j<f[i])f[i]=f[i-j*(j-1)/2]+j,pr[i]=i-j*(j-1)/2,pz[i]=j;
for(int i=1;i<=n;i++)
{
long long ns=s-i*x-i*(i-1)*y/2;
if(ns<0)break;
a[1]=x;
for(int j=2;j<=i;j++)a[j]=a[j-1]+y;
ns-=(n-i)*(x%y);
if(ns<0||ns%y!=0)continue;
ns/=y;
if(f[ns]>n-i)continue;
flag=0;
for(int j=i+1;j<=n-f[ns];j++)a[j]=x%y;
long long now=n-f[ns]+1;
while(ns)
{
for(long long j=0;j<pz[ns];j++)a[now+j]=x%y+j*y;
now+=pz[ns];
ns=pr[ns];
}
break;
}
if(flag)printf("NO\n");
else
{
printf("YES\n");
for(int i=1;i<=n;i++)printf("%lld ",a[i]);
printf("\n");
}
}
return 0;
}

浙公网安备 33010602011771号