【GDOI2018Day1模拟4.17】呼吸决定

Description:

这里写图片描述
1<=n<=10^9,1<=m<=200000

空间30720KB,时间1s,开O2

题解:

就是杜教筛+拉格朗日插值的结合题。

考场时没有打完,可惜了。

顺便回忆一下这两种算法。

s(n)=ni=1imμ(i)

ni=1imj|iμ(j)
=ni=1imnij=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=0yijiXxjxixj

显然这个东西展开后是n次的,并且把 xi 代进去是对的。

对于拉格朗日插值求自然数幂和,要处理一下 Xxj 的前后缀积,和阶乘的逆元。

这样就可以 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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值