CF1928E Modular Sequence 题解

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;
}
posted @ 2025-02-15 15:43  w9095  阅读(12)  评论(0)    收藏  举报