禁止动规-莫比乌斯+杜教筛

题目链接:https://ac.nowcoder.com/acm/problem/19836

Problem

给出一个数轴,刚开始人在位置0,有n个变量,每一个变量的值范围是[1,m],每次人可以选择其中一个变量,假设值为d,那么他可以向左或向右走d个单位长度,为最终在位置1的概率。

Solution

其实就是0+x1*a1+x2*a2+...+xn*an = 1.

根据裴蜀定理可知这个方程有整数解的条件是gcd(a1,a2...an) == 1.

有因为1<=ai<=m,所有得出公式:

\sum_{1}^{m}\sum_{1}^{m}...\sum_{1}^{m}gcd(a1...an) = 1

=\sum_{1}^{m}\sum_{1}^{m}...\sum_{1}^{m}\sum_{d|gcd(a1...an)}\mu (d)

=\sum_{1}^{m}\mu(d)\sum_{d|m}^{m}\sum_{d|m}^{m}...\sum_{d|m}^{m}

=\sum_{1}^{m}\mu(d)*(\left \lfloor m/d \right \rfloor)^{n}

由于m太大,所有先用杜教筛求出莫比乌斯函数的前缀和,m/d用数论分块求。

Code

#include <bits/stdc++.h>
#define ll unsigned long long
#define pir pair<int,int>
#define debug(x) cout << #x << ":" << x << '\n'
const int N = 1e7+7;
const ll mod = 1e9+7;
const ll ds = 1e15;
const double eps = 1e-8;
 
using namespace std;

ll prime[N],phi[N],mu[N];
map<long long,ll>mp;
int cnt = 0;
void init(){
    mu[1] = 1;
    for(int i = 2; i <= N; i++){
        if(!phi[i]){
            prime[cnt++] = i;
            phi[i] = i-1; 
            mu[i] = -1;
        }
        for(int j = 0; j < cnt && i*prime[j] <= N; j++){
            if(i%prime[j] == 0){
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            else {
                phi[i*prime[j]] = phi[i]*phi[prime[j]];
                mu[i*prime[j]] = -mu[i];
            }
        }
    }
        for(int i = 1; i <= N; i++){
            mu[i] += mu[i-1];
        }
}

ll djsmu(ll x){//杜教筛
    if(x < N) return mu[x];
    if(mp.count(x)) return mp[x];
    ll ans = 1;
    for(ll l = 2,r; l <= x; l = r+1){
        r = x/(x/l);
        ans -= (r-l+1)*djsmu(x/l);
    }
    return mp[x] = ans;
}

ll qpow(ll x,ll y){
    ll res = 1;
    while(y){
        if(y&1) res = res*x;
        y >>= 1;
        x = x*x;
    }
    return res;
}

void solve(){
    init();
    ll n,m,ans = 0;
    cin >> n >> m;
    for(ll l = 1,r; l <= m; l = r+1){
        r = m/(m/l);
        ans = (ans+(qpow(m/l,n)*(djsmu(r)-djsmu(l-1))));
    }
    //ans = (ans*qpow(m,n))%mod;
    cout << ans << endl;
}

int main(){
    // int t;
    // cin >> t;
    // while(t--)
        solve();
    system("pause");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值