CodeForces 1558B Up the Strip
有 n(2≤n≤4⋅106) 个节点,编号 1∼n,你一开始在节点 n,想要到节点 1。假设你现在在节点 x,你可以进行以下两种操作:
- 选择一个正整数 y∈[1,x−1],并移动到节点 x−y。
- 选择一个正整数 z∈[2,x],并移动到节点 ⌊zx⌋。
求有多少种方案到达节点 1,只要有一次选择的 x 或 z 不同就算方案不同。答案对 m 取模,其中 m∈(108,109) 且 m 是素数。
令 fi 表示 n=i 时的答案,我们有:
fi=j=1∑i−1fj+d=2∑if⌊di⌋
前一部分表示用减法,后一部分表示用除法。
考虑优化,第一部分直接前缀和,我们直接考虑第二部分。一种很直接的优化是将小于 i 的 d 直接拿出来算,大于 i 的 d 拿出来算贡献, fk 能转移到 fi 的 d 应该满足 k≤di<k+1,也即 k+1i<d≤ki,于是 fk 对 fi 的贡献应该为 ⌊kn⌋−⌊k+1n⌋。这样做复杂度是 O(nn),过不去,但是如果你打 Div. 2 的话就能过一个 easy 版 。
我们考虑换一种方式,考虑 fi 转移到 fi+1,我们比较 ∑d=2if⌊di⌋ 与 ∑d=2i+1f⌊di+1⌋,有如下变化:
- 多了一个 f1,因为 d 可以等于 i+1。
- 对于所有 d∣i+1,⌊di+1⌋=⌊di⌋+1,所以 fi+1 比 fi 多了一个 f⌊di+1⌋,少了一个 f⌊di⌋。
也就是说:
fi+1=j=1∑ifj+d=2∑i+1f⌊di+1⌋=fi+j=1∑i−1fj+d=2∑i+1f⌊di+1⌋=fi+fi+f1+d∣i+1∑(f⌊di+1⌋−f⌊di⌋)=2fi+f1+d∣i+1∑(f⌊di+1⌋−f⌊di⌋)
我们发现对于所有 i,总的约数个数为 ∑i=1nin=O(nlogn)。其余转移为 O(n),故总复杂度为 O(nlogn)。
考虑实现,在每次计算出 fi 的时候,将所有的 fki 都加上 fi−fi−1,这样空间复杂度就是 O(n)。
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
| const int maxn = 4000005;
int n, mod;
void add(int& x, int y) { x += y; if (x >= mod) { x -= mod; } else if (x < 0) { x += mod; } }
int f[maxn];
int main() { read(n), read(mod); f[1] = 1; for (int i = 2; i <= n; ++i) { int tmp = f[i - 1] << 1 | 1; if (tmp >= mod) { tmp -= mod; } add(f[i], tmp); if (i == 2) { f[i] = 2; } for (int j = i << 1; j <= n; j += i) { add(f[j], f[i]); add(f[j], -f[i - 1]); } } writeln(f[n]); return 0; }
|