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;
}
posted @ 2024-02-12 17:52  int_R  阅读(42)  评论(0编辑  收藏  举报