1. 程式人生 > 其它 >【CF1558B】Up the Strip

【CF1558B】Up the Strip

題目

題目連結:https://codeforces.com/problemset/problem/1558/B
你有一個正整數 \(n\),當 \(n>1\) 時,可以選擇以下兩種操作:

  1. 選擇一個在 \([1,n)\) 範圍內的整數 \(x\),讓 \(n\) 減去 \(x\)
  2. 選擇一個在 \([2,n]\) 範圍內的整數 \(x\),讓 \(n\) 除以 \(x\) 並下取整。

求有多少種方案使得 \(n\) 變為 \(1\)。答案對 \(m\) 取模。
\(n\leq 4\times 10^6\)\(m\) 是質數。(simplified version:\(n\leq 2\times 10^5\)

)。
時限 6s。

思路

\(f[i]\) 表示變到 \(i\) 的方案數。simplified version 的話由於 \(\lfloor\frac{i}{j}\rfloor\) 只有 \(O(\sqrt{i})\) 種取值,直接整除分塊就可以了。時間複雜度 \(O(n\sqrt n)\)
而這道題的話就不考慮刷表,考慮如何轉移到 \(f[i]\)。如果一個數除以 \(j\) 後下去整等於 \(i\),那麼這個數的範圍顯然是 \([ij,(i+1)j-1]\)。那麼直接列舉 \(j\),記一下字尾和即可。
時間複雜度 \(O(\sum_{i=1}^n \lfloor\frac{n}{i}\rfloor)=O(n\log n)\)

程式碼

/*
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
*/
#include <bits/stdc++.h>
#define YES printf("YES\n")
#define Yes printf("Yes\n")
#define NO printf("NO\n")
#define No printf("No\n")
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

const int N=4000010;
int Q,n,MOD,f[N],g[N];

int main()
{
	//cerr<<(sizeof(f)+sizeof(g))/1024/1024<<"\n\n";
	scanf("%d%d",&n,&MOD);
	f[n]=g[n]=1;
	for (int i=n-1;i>=1;i--)
	{
		f[i]=g[i+1];
		for (int j=2;i*j<=n;j++)
		{
			int l=i*j,r=min(n,(i+1)*j-1);
			if (l>r) continue;
			f[i]=((f[i]+g[l])%MOD-g[r+1])%MOD;
		}
		g[i]=(g[i+1]+f[i])%MOD;
	}
	cout<<(f[1]%MOD+MOD)%MOD;
	return 0;
}