CF1561D1 Up the Strip (simplified version) 题解.md

容易想到 f i f_i fi​ 表示走到 i i i 的方案数和转移方程(一开始我这个蒟蒻居然还觉着是刷表呢
f x = ( ∑ i = x + 1 n f i ) + ( ∑ { f y ∣ x = ⌊ y k ⌋ } ) f_x=(\sum_{i=x+1}^nf_i)+(\sum\{f_y | x=\lfloor \frac{y}{k} \rfloor\}) fx​=(i=x+1∑n​fi​)+(∑{fy​∣x=⌊ky​⌋})
第一项很好求,维护一个后缀和,难点主要是在第二项

我们可以枚举 k k k ,不超过 n \sqrt{n} n ​ 个,这时我们的 k k k 固定下来了,显然 y ∈ [ x k , x k + k ) y\in [xk,xk+k) y∈[xk,xk+k) 这样余数会被下取整抹掉,这样的话我们可以用后缀和维护这一段的和,于是我们的状态数是 O ( n ) O(n) O(n) 的,转移是枚举一个 k k k ,是 O ( n ) O(\sqrt n) O(n ​) 的,总的复杂度就是 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23​)

(考试的时候还推出一个没什么用的结论,就是 [ x k , x k + k ) [xk,xk+k) [xk,xk+k) 之间的数不会因为第二个操作而互相影响,因为 2 x y ≥ x k + k 2xy \ge xk+k 2xy≥xk+k 以后可能用得到吧)

比赛完跟一位大佬交流才知道这个玩意叫数论分块,比赛完发现好多分都是在这题比别人少的,然而我太弱了都不知道数论分块是啥(悲),好在推出来了,还推出了一个没什么用的结论

代码:

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int, int>
#define mem(a, x) memset(a, x, sizeof(a))
#define rps(i, b, e) for(int i=(b);i<=(e);i++)
#define prs(i, e, b) for(int i=(e);i>=(b);i--)
#define rp(i, e) rps(i, 1, e)
#define pr(i, e) prs(i, e, 1)
#define rp0(i, e) rps(i, 0, (e)-1)
#define pr0(i, e) prs(i, (e)-1, 0)
#define rpg(i, x) for(int i=head[x];i;i=e[i].nxt)
#define opf(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
using namespace std;
const int NR=2e5+10;
typedef long long LL;
LL n, p, f[NR], s[NR];
int main()
{
	cin>>n>>p;
	f[n]=s[n]=1;
	pr(i, n-1) {
		f[i]=s[i+1];
		for(LL j=2;i*j<=n;j++) {
			f[i]=(f[i]+(s[i*j]+p-s[min(i*j+j, n+1)]))%p; 
		}
		s[i]=(s[i+1]+f[i])%p;
	}
	cout<<f[1];
	return 0;
}
上一篇:等离子体技术【八】--RPS远程等离子蚀刻机台


下一篇:基于RPS开发模式的可视化设计解决方案