Description:
1<=n<=10^9,1<=m<=200000
空间30720KB,时间1s,开O2
题解:
就是杜教筛+拉格朗日插值的结合题。
考场时没有打完,可惜了。
顺便回忆一下这两种算法。
设 s(n)=∑ni=1im∗μ(i)
∑ni=1im∑j|iμ(j)
=∑ni=1im∑⌊ni⌋j=1μ(j)∗jm
=∑ni=1ims(⌊ni⌋)
提出i=1的情况,移项,得:
s(1)=∑ni=2ims(⌊ni⌋)
预处理 n2/3 内的结果,之后的分块+hash。
现在问题在于快速求 ∑ni=1im 。
差分法可得这是一个m+1次多项式,于是我们可以通过插m+2个值来求解。
一般插0-m+1会好一点,注意一定是0-m+1。
拉格朗日插值:
f(X)=∑ni=0yi∏j≠iX−xjxi−xj
显然这个东西展开后是n次的,并且把 xi 代进去是对的。
对于拉格朗日插值求自然数幂和,要处理一下 X−xj 的前后缀积,和阶乘的逆元。
这样就可以 O(m) 算一次了。
这个预处理500000个,剩下200个,200*200000=400000,追求空间与时间的均衡。
Code:
#include<cstdio>
#include<bitset>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int mo = 998244353;
const int N = 1e6, C = 2e5 + 1, E = 5e6 + 5;
int n, m;
ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
}
bitset<N + 1> bz;
int p0[350000], mu[N + 1];
int nf[C + 1], p[C + 1], q[C + 1], cm[E + 1];
void BB() {
mu[1] = 1; cm[1] = 1;
fo(i, 2, N) {
if(!bz[i]) p0[++ p0[0]] = i, mu[i] = -ksm(i, m);
fo(j, 1, p0[0]) {
int k = i * p0[j]; if(k > N) break;
bz[k] = 1;
if(i % p0[j] == 0) {mu[k] = 0; break;}
mu[k] = (ll) mu[i] * mu[p0[j]] % mo;
}
}
fo(i, 1, N) mu[i] = (mu[i - 1] + mu[i]) % mo;
ll fac = 1;
fo(i, 1, C) fac = fac * i % mo, nf[i] = ksm(fac, mo - 2);
nf[0] = 1;
fo(i, 1, E) cm[i] = (cm[i - 1] + ksm(i, m)) % mo;
m ++;
}
const int M = 131321;
int h2[M][2];
int ha2(int x) {
int y = x % M;
while(h2[y][0] && h2[y][0] != x) y = (y + 1) % M;
return y;
}
ll sum(ll n) {
if(n <= E) return cm[n];
int y = ha2(n);
if(h2[y][0] == n) return h2[y][1];
h2[y][0] = n;
ll ans = 0;
p[0] = n; fo(i, 1, m) p[i] = (ll) p[i - 1] * (n - i) % mo;
q[m + 1] = 1; fd(i, m, 0) q[i] = (ll) q[i + 1] * (n - i) % mo;
fo(i, 1, m) ans += (ll) cm[i] * p[i - 1] % mo * q[i + 1] % mo * nf[i] % mo * nf[m - i] * ((m - i) % 2 ? -1 : 1) % mo;
ans = (ans % mo + mo) % mo;
h2[y][1] = ans;
return ans;
}
int h[M][2];
int ha(int x) {
int y = x % M;
while(h[y][0] && h[y][0] != x) y = (y + 1) % M;
return y;
}
ll dg(int n) {
if(n <= N) return (mu[n] + mo) % mo;
int y = ha(n);
if(h[y][0] == n) return h[y][1];
h[y][0] = n;
ll ans = 1;
fo(i, 2, n) {
int j = n / (n / i);
ans -= dg(n / i) * (sum(j) - sum(i - 1) + mo) % mo;
i = j;
}
ans = (ans % mo + mo) % mo; h[y][1] = ans;
return ans;
}
int main() {
freopen("calc.in", "r", stdin);
freopen("calc.out", "w", stdout);
scanf("%d %d", &n, &m);
BB();
printf("%lld\n", dg(n));
}