bzoj3309 DZY Loves Math

bzoj3309


前戏

ai=1bj=1f(gcd(i,j))
=ad=1ai=1bj=1e(gcd(i,j)==d)f(d)
=ad=1a/di=1b/dj=1e(gcd(i,j)==1)f(d)
=ad=1a/di=1b/dj=1d|id|jμ(d)f(d)
=ad=1f(d)a/dd=1μ(d)a/ddi=1b/ddj=11
=ad=1f(d)a/dd=1μ(d)addbdd
考虑枚举dd’
=ai=1aibid|if(d)μ(i/d)
很显然f虽然不是积性函数但是可以被筛出来,
对于后面一部分 g(i)=d|if(d)μ(i/d) 我一开始是准备埃氏筛法,但是时限太紧了TAT
然而这玩意儿一副欧拉筛不可做的样子。

解决

然后我们就可以发现我们是不用考虑mu值等于0的那一部分。
考虑 i=qr11qr22qrtt 假设其中 rw 最大且一共有k个r是与 rw 相等的.
很显然,对于我们考虑的 μ(d) ,d是 {q1,q2,qn} 这个集合的一个子集内所有元素相乘的一个值.
我们可以将这些子集按包不包含元素 rw 来分成两两一组(也就是说每一组除去 rw 都应该是一样的),假设除去 rw 后集合为 T 如果j,rjT那么显然他们对应的f值是相等的,也就是说这一对对g(i)的贡献相加=0.
所以我们应该考虑的是所有包含了这k个元素的集合,那么最后的答案应该是
t1i=k1Ci(k1)t1(k1)(1)i+1(ans+1)Ci(k1)t1(k1)(1)ians
= t1i=k1Ci(k1)t1(k1)(1)i=tki=0Citk(1)i+k1
t=k 时,原式= (1)k+1
tk 时,根据二项式定理,原式 =(1+1)tk=0
所以我们如果能统计出每个数对应的t和k就搞定辣,这个很好筛出来的.
发现标程有辣么短,自己好长QAQ

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<ctime>
const int N = 1e7 + 7;

typedef long long ll;
bool vis[N];
int prime[N], tot, wor[N], wor2[N], sl[N], num[N], jx[N];

void G (int &num) {
    static char a;
    for (a = getchar (); a > '9' || a < '0'; a = getchar ()) ;
    for (num = 0; a >= '0' && a <= '9'; a = getchar ()) num = (num << 3) + (num << 1) + a - '0';
}

void Pre () {
    vis[1] = true;
    for (int i = 2; i <= 10000000; ++i) {
        if (!vis[i]) prime[++tot] = i, wor[i] = wor2[i] = sl[i] = num[i] = 1;
        for (int j = 1; j <= tot && 1ll * i * prime[j] <= 10000000; ++j) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) {
                sl[i * prime[j]] = sl[i] + 1;
                wor[i * prime[j]] = wor[i];
                if (sl[i * prime[j]] == wor2[i]) num[i * prime[j]] = num[i] + 1, wor2[i * prime[j]] = wor2[i]; 
                else if (sl[i * prime[j]] > wor2[i]) num[i * prime[j]] = 1, wor2[i * prime[j]] = sl[i * prime[j]];
                else num[i * prime[j]] = num[i], wor2[i * prime[j]] = wor2[i];
                break;
            }
            sl[i * prime[j]] = 1;
            wor[i * prime[j]] = wor[i] + 1;
            wor2[i * prime[j]] = wor2[i];
            num[i * prime[j]] = num[i];
            if (wor2[i] == 1) ++num[i * prime[j]];
        }
        jx[i] = jx[i - 1];
        if (wor[i] == num[i]) jx[i] += num[i] & 1 ? 1 : -1;
    }
}

ll calc (int a, int b) {
    ll ret = 0;
    for (int i = 1, lo = 1; i <= a; i = lo + 1) {
        lo = std :: min (a / (a / i), b / (b / i));
        ret += 1ll * (jx[lo] - jx[i - 1]) * (a / i) * (b / i);
    }
    return ret;
}

void Solve () {
    int T, a, b;
    G (T);
    while (T--) {
        G (a), G (b);
        if (a > b) a ^= b ^= a ^= b;
        printf ("%lld\n", calc (a, b));
    }
}

void IO () {
    freopen ("3309.in", "r", stdin);
    freopen ("3309.ans", "w", stdout);
}

int main () {
    IO ();
    Pre ();
    Solve ();
    return 0;
}

update :
看了别人的代码,仔细研究了一番,发现我的信息记得过于全面,还是有很多玩意儿是可以省略的,现在附上简洁版的代码

/**************
+1
written by DraZxlNDdt
Accepted
***************/ 
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<ctime>
const int N = 1e7 + 7;

typedef long long ll;
bool vis[N];
int prime[N], tot, sl[N], jx[N], zx[N], g[N];

void G (int &num) {
    static char a;
    for (a = getchar (); a > '9' || a < '0'; a = getchar ()) ;
    for (num = 0; a >= '0' && a <= '9'; a = getchar ()) num = (num << 3) + (num << 1) + a - '0';
}

void Pre () {
    vis[1] = true;
    for (int i = 2; i <= 10000000; ++i) {
        if (!vis[i]) prime[++tot] = i, zx[i] = i, sl[i] = g[i] = 1;
        for (int j = 1; j <= tot && 1ll * i * prime[j] <= 10000000; ++j) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) {
                sl[i * prime[j]] = sl[i] + 1;
                zx[i * prime[j]] = zx[i] * prime[j];
                if (i == zx[i]) g[i * prime[j]] = 1;
                else g[i * prime[j]] = sl[i * prime[j]] == sl[i / zx[i]] ? -g[i / zx[i]] : 0;
                break;
            }
            sl[i * prime[j]] = 1;
            zx[i * prime[j]] = prime[j];
            g[i * prime[j]] = sl[i] == 1 ? -g[i] : 0;
        }
        jx[i] = jx[i - 1];
        if (g[i]) jx[i] += g[i];
    }
}

ll calc (int a, int b) {
    ll ret = 0;
    for (int i = 1, lo = 1; i <= a; i = lo + 1) {
        lo = std :: min (a / (a / i), b / (b / i));
        ret += 1ll * (jx[lo] - jx[i - 1]) * (a / i) * (b / i);
    }
    return ret;
}

void Solve () {
    int T, a, b;
    G (T);
    while (T--) {
        G (a), G (b);
        if (a > b) a ^= b ^= a ^= b;
        printf ("%lld\n", calc (a, b));
    }
}

void IO () {
    freopen ("3309.in", "r", stdin);
    freopen ("3309.ans", "w", stdout);
}

int main () {
//  IO ();
    Pre ();
    Solve ();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值