根号分治优化dp——暴力dp +类整数划分:0103C

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/135402981

http://47.92.197.167:5283/contest/441/problem/3

首先有个暴力dp。 f(i,j)f(i,j) 和为 ii ,末项为 jj

看到这种和达到平方级别,比如初项和长度成反比的题目,应该要想到根号分治

我们随便分,比如初项 B\le B ,那 jj 不超过 2B2B ,直接跑即可。

如果 >B>B ,我们考虑类整数划分思路。 g(i,j)g(i,j) 表示现在到第 ii 个,所有数总和为 jj ,且 [i,n][i,n]i1i-1 目前相同。则现在对于 ii ,我们可能±1,为了保证正确性,我们让后面的整体±1,也就是转移到 g(i+1,j+i),g(i+1,ji)g(i+1,j+i),g(i+1,j-i) 。显然长度不超过 O(nB)O(\frac n B)

空间方面,第二个直接滚。第一个发现 ii 可以按 mod2B\bmod\, 2B 的余数分类。因此空间是线性的。

然后我被卡常了。

1
2
3
4
5
pre coding at 
st coding at 15:28
st bugging at 15:54
passing at
fn blogging at
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
#define int long long
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
#define Z(x) (x) * (x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244353
//#define N
#define B 623
inline void Mod(int &a) {
if (a >= mo || a <= -mo)
a %= mo;
if (a < 0)
a += mo;
}
inline void Add(int &a, int b) {
a += b;
Mod(a);
}
int n, m, i, j, k, T, W;
int f[B * 3 + 10][3 * B + 10];
int g[2][300010];
int ans, M;

namespace Sol2 {
int ans = 0, s;
void Main() {
for (i = 1; i <= n; ++i) {
s = (1 + i) * i / 2;
if (s > n)
continue;
if ((n - s) % i == 0)
Add(ans, 1);
}
printf("%lld\n", ans);
}
} // namespace Sol2

signed main() {
freopen("dazzling.in", "r", stdin);
freopen("dazzling.out", "w", stdout);
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// srand(time(NULL));
// T=read();
// while(T--) {
//
// }
n = read();
W = read();
M = 3 * B;
if (W == 0)
return Sol2 ::Main(), 0;
for (i = 1; i <= n; ++i) {
for (j = 1; j <= 3 * B && j <= i; ++j) {
if (i == j && i <= B)
f[i % M][j]++;
Add(f[(i + j + 1) % M][j + 1], f[i % M][j]);
if (j > 1)
Add(f[(i + j - 1) % M][j - 1], f[i % M][j] * W % mo);
}
if (i == n) {
for (j = 1; j <= 3 * B && j <= i; ++j) Add(ans, f[n % M][j]);
}
for (j = 1; j <= 3 * B && j <= i; ++j) f[i % M][j] = 0;
}
for (i = 3 * B; i >= 0; --i) {
for (j = B + 1; j * (i + 1) <= 3 * n; ++j) g[i & 1][j * (i + 1)]++;
if (!i)
break;
// debug("%lld : 1246[%lld] 1248[%lld]\n", i, g[i & 1][1246], g[i & 1][1248]);
for (j = 1; j <= 3 * n; ++j) {
if (j + i <= 3 * n)
Add(g[(i - 1) & 1][j + i], g[i & 1][j] % mo);
if (j - i >= 1)
Add(g[(i - 1) & 1][j - i], g[i & 1][j] * W % mo);
g[i & 1][j] = 0;
}
}
debug(">> %lld\n", g[0][n]);
Add(ans, g[0][n]);
printf("%lld\n", ans);
return 0;
}