【bzoj 3512】DZY loves math IV

ni=1mj=1ϕ(ij) n 不大。

n不大摆明了就是暴力对吧。
考虑 S(n,m)=mi=1ϕ(in) ,我们先来看一个搞笑做法。。。
首先 n 内幂指数大于1的质因子可以拎出来到只剩一次,于是我们只考虑μ(n)0 n 。接着考虑取n的任意一个质因子 p ,对于pi的指标 i ,我们规定此时p ϕ(in) 的贡献为 p p1的贡献由 i 提供,于是也可以提出来。而对于pi的指标 i p的贡献即为 p1 。综合两个考虑,如果我们直接把前一类的 i 当做后一类来处理了,那么我们就要补回1的贡献。于是我们有

S(n,m)=(p1)i=1mϕ(npi)+i=1m[i%p==0]ϕ(imp)=(p1)S(n/p,m)+S(n,m/p)

递归终止条件是 n==1 ,然后上杜教筛。看起来很靠谱。。?
然而仔细想想这就是个。。。爆搜。。。对吧= =但是又因为 n 很小所以不同的质因子数也很少。。最多只有6。。。。所以总共可能的状态大概只有2kw个左右。。
于是我就写了这个玩意。。。然后华丽的狗带了。。。冷静了一下发现。。。空间限制只有128MB?那我2kw个数挂链表都存不下啊。。?一番剪枝剪到了1.3kw。。再也卡不动空间了。。当然如果有谁能卡过去那他很niubia。。。于是重新推了推式子看看能不能搞搞别的。。。

下面是方法二。
考虑直接展开ϕ(in)会是个什么东西。
d=gcd(i,n) ,因为 μ(n)0 所以也有 μ(d)0 。。我们单独考虑一个 d 的质因子p,而前面那个条件就允许我们直接令 phi i p所贡献的乘积中包含了 p1 。。因此我们希望 n 中的p贡献要恰好为 p 。。。而实际上这也很好处理。。。当phi(x)中包含 p 时,p的贡献为 p1 ,否则为 1 ,而又由于他是个积性的玩意所以每个质因子的贡献独立。。。所以如果我们算kdϕ(n/k)的话 p 在这里面的贡献就刚好为p了。因此我们得到 ϕ(in)=ϕ(i)kgcd(i,n)ϕ(n/k)
然后一波代入式子化简就可以得到 S(n,m)=knϕ(n/k)S(k,m/k) ,这样复杂度就是对的了。
你问我复杂度?大概是 O(n3/2+m2/3) 吧。。

代码:


arr mn;
adj vis , phi , pr;
int ptt;

int n , m , N;

void input() {
    n = rd() , m = rd();
    N = (int) (pow(m , 0.7) + 0.5);
    upmax(N , n);
    rep (i , 2 , N) {
        if (!vis[i]) {
            pr[++ ptt] = i;
            phi[i] = i - 1;
            if (i <= n)
                mn[i] = i;
        }
        for (int j = 1; j <= ptt && i * pr[j] <= N; j ++) {
            int t = i * pr[j];
            vis[t] = 1;
            if (t <= n) mn[t] = min(mn[i] , pr[j]);
            if (i % pr[j] == 0) {
                phi[t] = pr[j] * phi[i];
                break;
            }
            phi[t] = (pr[j] - 1) * phi[i];
        }
    }
    phi[1] = 1;
    rep (i , 2 , N) phi[i] = add(phi[i] , phi[i - 1]);
}

inline int get(int x) {
    int t = 1;
    while (x > 1) {
        int y = mn[x];
        x /= y;
        t *= y;
        while (x % y == 0) x /= y;
    }
    return t;
}

__gnu_pbds::gp_hash_table<int , int> ms;

int dls(int n) {
    if (n <= N)
        return phi[n];
    if (ms.find(n) != ms.end())
        return ms[n];
    int ret = 1ll * n * (n + 1) / 2 % mod;
    for (int l = 2 , r = 0; l <= n; l = r + 1) {
        r = n / (n / l);
        ret = dec(ret , mul(r - l + 1 , dls(n / l)));
    }
    ms[n] = ret;
    return ret;
}

__gnu_pbds::gp_hash_table<int , int> exi[maxn];

int gao(int n , int m) {
    if (n == 1)
        return dls(m);

    if (!m) return 0;
    if (m == 1) return phi[n] - phi[n - 1];
    if (exi[n].find(m)!=exi[n].end()) return exi[n][m];

    int t = 0;
    for (int i = 1; i * i <= n; i ++) if (n % i == 0) {
        int j = n / i;
        t = add(t , mul(phi[j] - phi[j - 1] , gao(i , m / i)));
        if (i != j) {
            t = add(t , mul(phi[i] - phi[i - 1] , gao(j , m / j)));
        }
    }

    exi[n][m]=t;

    return t;
}

void solve() {
    int ans = 0;
    rep (i , 1 , n) {
        int x = get(i);
        int tmp = gao(x , m);
        ans = add(ans , mul(i / x , tmp));
    }
    printf("%d\n" , ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值