整除分块前缀和DPD. Up the Strip(简单版 + 正常版)

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整除分块前缀和DPD. Up the Strip(简单版 + 正常版)相关的知识,希望对你有一定的参考价值。

整除分块

给出一道例题,已知n, 求 ∑ i = 1 n ⌊ n i ⌋ \\sum_i=1^n \\lfloor\\fracni \\rfloor i=1nin

这就是整除分块的基本例题

画一个表格找一下怎么计算,以n = 15为例

i123456789101112131415
⌊ 15 i ⌋ \\lfloor \\frac15i\\rfloor i151575332211111111

证明:

假设分块的左端点为l,要求分块的右端点r.

设分块的值为k,对于区间 [ l , r ] [l,r] [l,r]的每个数满足 k = ⌊ n i ⌋ = ⌊ n l ⌋ k=\\lfloor \\fracni\\rfloor=\\lfloor \\fracnl\\rfloor k=in=ln, 即 k i ≤ n ki \\leq n kin,需要找到最大的i使其成立

可得 r = ⌊ n k ⌋ = ⌊ n n l ⌋ r = \\lfloor \\fracnk\\rfloor=\\lfloor \\fracn \\fracnl\\rfloor r=kn=lnn

计算的相关代码如下:

每次计算出相同值的左右端点 [ l , r ] [l, r] [l,r] ,那么相同值的个数就为 r − l + 1 r-l+1 rl+1

int res = 0;
for(int l = 1, r; l <= n; l = r + 1)

    r = n / (n / l);
    res += n / l * (r - l + 1);

题目

简单版

链接:

https://codeforces.com/problemset/problem/1561/D1


状态表示:

f [ i ] f[i] f[i]: i变为1的种类数

状态转移:

f [ i ] = ∑ j = 1 i − 1 f [ j ] + ∑ j = 2 i f [ ⌊ i j ⌋ ] f[i] = \\sum_j=1^i-1f[j] + \\sum_j=2^if[ \\lfloor \\fracij\\rfloor ] f[i]=j=1i1f[j]+j=2if[ji]

前一部分是考虑减法的方程,后一部分是考虑除法的方程

  • 减法:可以使用前缀和进行优化
  • 除法: ⌊ i j ⌋ \\lfloor \\fracij\\rfloor ji 考虑使用整除分块

复杂度为 O ( n n ) O(n \\sqrt n) O(nn )

#include<bits/stdc++.h>
using namespace std;

using ll = long long;


void solve()

	int n, m;
	cin >> n >> m;
	
	vector<ll> f(n + 1, 0), s(n + 1, 0);
	f[1] = 1;
	s[1] = 1;
	
	for(int i = 2; i <= n; i++)
	
		f[i] = (f[i] + s[i - 1]) % m;
		
		for(int l = 2, r; l <= i; l = r + 1)
		
			r = i / (i / l);
			int cnt = r - l + 1;
			
			ll x = f[i / l] * cnt % m;
			f[i] = (f[i] + x) % m;
		

		s[i] = s[i - 1]	+ f[i];
		s[i] %= m;
	
	cout << f[n] % m << "\\n";
	


int main()

	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
//	cin >> t;
	t = 1;
	while(t--) 
		solve();
	return 0;

 

正常版

https://codeforces.com/contest/1561/problem/D2


这道题n的限制变大,整除分块无法过去

反过来考虑,从小到大进行考虑

状态表示:

f [ i ] f[i] f[i] i变到n的方案数

状态转移:

f [ i ] = ∑ j = i + 1 n f [ j ] + ∑ j = 2 j ∗ i ≤ n ∑ k = i ∗ j m i n ( i ∗ j + j − 1 , n ) f [ k ] f[i] = \\sum_j=i+1^nf[j] + \\sum_j=2^j*i\\leq n \\sum_k=i*j^min(i*j+j-1,n)f[k] f[i]=j=i+1nf[j]+j=2jink=ijmin(ij+j1,n)f[k]

状态转移公式可能看着很难懂,下面进行解释:

  • 前一部分:通过加法变到j的方案和,i可以变到 [ i + 1 , n ] [i+1,n] [i+1,n]的任意一个
    • 计算方法:可以通过后缀和进行计算
  • 后一部分:通过乘法变到k的方案数,因为题目是除法,我们反过来就变成了乘法,除法进行考虑,我们找一下原始的一个区间,区间中的每一个值可以通过除法变到同一个值
    • 若除2,区间 [ 2 ∗ i , 2 ∗ i + 1 ] [2*i, 2*i+1] [2i,2i+1]中的数可以变到i, 计算次数为 n / 2 n/2 n/2
    • 若除3,区间 [ 3 ∗ i , 3 ∗ i + 2 ] [3*i, 3*i+2] [3i,3i+2]中的数可以变到i,计算次数为 n / 3 n/3 n/3
    • 若除4,区间 [ 4 ∗ i , 4 ∗ i + 3 ] [4*i, 4*i+3] [4i,4i+3]中的数可以变到i,计算次数为 n / 4 n/4 n/4
    • 若除j,区间 [ j ∗ i , j ∗ i + j − 1 ] [j*i, j*i+j-1] [ji,ji+j1]中的数可以变到i,计算次数为 n / j n/j n/j
    • 统计方法:后缀和: s [ i ∗ j ] − s [ m i n ( i ∗ j + j , n + 1 ) ] s[i * j] - s[min(i * j + j, n + 1)] s[ij]s[min(ij+j,n+1)]

总的计算次数就是 n / 2 + n / 3 + n / 4 + . . . + n / n = n ( 1 / 2 + 1 / 3 + . . . + 1 / n ) n/2+n/3+n/4+...+n/n=n(1/2+1/3+...+1/n) n/2+n/3+n/4+...+n/n=n(1/2+1/3+...+1/n) 后面的是调和级数,复杂度为 l o g ( n ) log(n) log(n),故总的复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(n以上是关于整除分块前缀和DPD. Up the Strip(简单版 + 正常版)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 6555 The Fool(打表&整除分块)

[Bzoj 2956] 模积和 (整除分块)

D2. Up the Strip(递推)

cf1561D Up the Strip(D1&&D2)

筛法应用CF1558B - Up the Strip

筛法应用CF1558B - Up the Strip