Codeforces 1928E Modular Sequence 题解 [ 蓝 ] [ 线性 DP ] [ sqrt trick ]
Modular Sequence:感觉是比较经典的观察了。
首先转化一下题意,等效于求一个长度为 \(n\) 的序列,序列由多个首项为 \(x\),公差为 \(y\) 的等差数列拼接而成,并且总和为 \(s\)。
发现首项为 \(x\) 非常地不优美,所以我们可以把所有数减去 \(x\bmod y\) 来让所有等差数列的首项为 \(0\)。注意第一段等差数列是特殊的,因为规定了第一个数必须是 \(x\)。
问题被转化为是否存在一个长为 \(n\) 的序列,按上述方法拼成,且总和为 \(s\)。因为我们可以用多个长度为 \(1\) 的等差数列(和为 \(0\))填补没有填满的位置,所以只需要判断能否让这个序列的长度 \(\le n\) 即可。于是设计 \(dp_i\) 表示总和为 \(i\) 的时候,序列最短的长度。转移直接枚举前驱即可,时间复杂度 \(O(n^2)\)。
注意到每次加的是一个等差数列,根据其求和公式 \(\dfrac{y\times(len-1)\times len}{2}\),容易发现是平方级别增长的。也就是说,当 \(len = \sqrt{s}\) 左右时,和已经差不多大于 \(s\) 了。所以没必要枚举那么多前驱,直接枚举到 \(\sqrt{s}\) 的位置即可。
剩下的构造就是容易的了,枚举特殊的第一段的长度,然后用 DP 数组判断能否构造出后面的序列;如果能,则根据 DP 数组转移的前驱构造;否则就说明无解。
时间复杂度 \(O(n + s\sqrt{s})\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 200005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll dp[N], n, x, y, s, a[N], pre[N];
void upd(int i, int j, int len)
{
if(dp[i] > dp[j] + len)
{
dp[i] = dp[j] + len;
pre[i] = j;
}
}
void solve()
{
cin >> n >> x >> y >> s;
s -= n * (x % y);
ll bs = x % y;
x -= (x % y);
dp[0] = 0;
for(int i = 1; i <= s; i++)
{
dp[i] = inf;
for(int j = 1; 1ll * y * (j - 1) * j / 2 <= i; j++)
upd(i, i - (1ll * y * (j - 1) * j / 2), j);
}
for(int i = 1; i <= n; i++)
{
ll cur = s - (((2ll * x) + (i - 1) * y) * i / 2);
if(cur < 0) continue;
if(dp[cur] <= n - i)
{
cout << "YES\n";
for(int j = 1; j <= i; j++) cout << bs + x + (j - 1) * y << " ";
int now = cur;
while(now)
{
int predp = pre[now];
int len = dp[now] - dp[predp];
for(int j = 1; j <= len; j++) cout << bs + y * (j - 1) << " ";
now = predp;
}
for(int j = 1; j <= n - i - dp[cur]; j++) cout << bs << " ";
cout << "\n";
return;
}
}
cout << "NO\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号