CF1928E Modular Sequence
设 \(p=x\bmod y\)。思考发现本质是 \(x,x+y,x+2y,\cdots,x+k_1y,p,p+y,p+2y,\cdots,p+k_2y,p,p+y,p+2y,\cdots,p+k_3y\cdots\),即每次二操作会使 \(y\) 的系数变为 \(0\)。
枚举第 \(i\) 次操作是第一次二操作,记 \(s_1=s-(i\times x+y\times\dfrac{i(i-1)}{2}+(n-i)\times p)\),也就是第一次二操作后通过若干个 \(y\) 凑出来的数,当 \(s1<0\) 或者 \(y\nmid s1\) 时不合法。
那么我们现在要使系数和为 \(\dfrac{s1}{y}\) 且操作次数不大于 \(n-i\) 次,所以可以用一个背包来找出凑出系数 \(j\) 的最小操作数。连续的 \(1\) 次二操作和 \(i-1\) 次一操作可以获得 \(\dfrac{i*(i-1)}{2}\) 的贡献,可知有用的 \(i\) 是根号级别的,直接背包,然后记录一下 DP 路径,最后就可以输出方案了。注意如果最少操作数小于 \(n-i\),那么要多进行几次二操作来补满。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int MAXN=2e5+10,INF=1e9+7;
int T,n,x,y,s,f[MAXN],d[MAXN];
inline bool B(int k,int s)
{
if(s<0||s%y) return false;
if(f[s/y]>k) return false;
s/=y;cout<<"Yes\n";
for(int i=1;i<=n-k;++i) cout<<x+(i-1)*y<<' ';
for(int i=1;i<=k-f[s];++i) cout<<x%y<<' ';
while(s)
{
for(int i=1;i<=d[s];++i)
cout<<x%y+(i-1)*y<<' ';
s-=d[s]*(d[s]-1)/2;
}
cout<<'\n';return true;
}
inline void work()
{
cin>>n>>x>>y>>s;bool flag=false;
for(int i=1;i<=n;++i)
if(B(n-i,s-(x*i+i*(i-1)/2*y+(n-i)*(x%y))))
{flag=true;break;}
if(!flag) cout<<"No\n";return ;
}
signed main()
{
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
for(int i=1;i<=200000;++i) f[i]=INF;
for(int i=1;;++i)
{
int cur=i*(i-1)/2;if(cur>200000) break;
for(int j=cur;j<=200000;++j)
{
if(f[j-cur]+i<f[j])
f[j]=f[j-cur]+i,d[j]=i;
}
}
cin>>T;while(T--) work();
return 0;
}