题意
求
题解
这个题第一眼看上去像个容斥,于是我整除分块后跑了个
for (ll i=1, d; i<=n; i=n/d+1)
d=n/i, w[++m]=i;
f[0][1]=1;
for (int i=1; i<=k; i++)
{
if (a[i]==1) { puts("0"); return 0; }
for (int j=m, p=m; j>=1; j--)
{
ll tmp=w[j]*a[i];
if (tmp>n) continue;
while (w[p]>tmp) p--;
// 因为 long long 除法很慢就避免了使用除法
f[0][p]+=f[1][j];
f[1][p]+=f[0][j];
}
}
ll ans=0;
for (int i=1; i<=m; i++)
ans+=n/w[i]*(f[0][i]-f[1][i]);
printf("%lld\n", ans);
然后交了一发发现 T 了,才看到题目旁边写了时限减半。。只好去看正解。
设
直接 DP 是
状态数少还有一个原因是,
而我的方法无法被优化是因为所有状态都是最终要用的,记忆化搜索不如递推。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXK=110;
const int MAXM=110000;
const int M=1E5;
int a[MAXK], f[MAXM][MAXK];
ll dp(ll n, int k)
{
if (k==0) return n;
if (n<=M&&~f[n][k]) return f[n][k];
ll ret=dp(n, k-1)-dp(n/a[k], k-1);
if (n<=M) f[n][k]=ret;
return ret;
}
int main()
{
// freopen("CF93E.in", "r", stdin);
// freopen("CF93E.out", "w", stdout);
ll n; int k;
scanf("%lld%d", &n, &k);
for (int i=1; i<=k; i++) scanf("%d", &a[i]);
sort(a+1, a+k+1);
memset(f, -1, sizeof f);
printf("%lld\n", dp(n, k));
return 0;
}