【HDU 6053 TrickGCD】 + 莫比乌斯反演

TrickGCD

Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1002 Accepted Submission(s): 389

Problem Description
You are given an array A , and Zhu wants to know there are how many different array B satisfy the following conditions?

  • 1≤Bi≤Ai
  • For each pair( l , r ) (1≤l≤r≤n) , gcd(bl,bl+1…br)≥2

Input
The first line is an integer T(1≤T≤10) describe the number of test cases.

Each test case begins with an integer number n describe the size of array A.

Then a line contains n numbers describe each element of A

You can assume that 1≤n,Ai≤105

Output
For the kth test case , first output “Case #k: ” , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer mod 109+7

Sample Input
1
4
4 4 4 4

Sample Output
Case #1: 17

Source
2017 Multi-University Training Contest - Team 2

题意 : b[i] 可以从 1 ~ a[i] 之间选个数,任意 gcd(b[l] ~ b[r]) > 1,可以有多少中 b 数组

思路 : 枚举每个因数 o,对于 b,可以选择的数有 o * 1,o * 2,o * 3…o * b / o,有 b / o 个,对整个数组来说 贡献所有 b / o 的乘积, 预处理一下 l ~ r 之间有多少数,可以算出 o * r ~ o * (r + 1) - 1 之间的数的贡献都为 r,快速幂一下,最后利用 莫比乌斯反演去重

莫比乌斯反演是数论中的重要内容,在许多情况下能够简化运算
我们需要找到f(n)与F(n)之间的关系。从和函数定义当中,我们可以知道:
F(1)=f(1)
F(2)=f(1)+f(2)
F(3)=f(1)+ f(3)
F(4)=f(1)+f(2)+f(4)
F(5)=f(1)+f(5)
F(6)=f(1)+f(2)+f(3)+f(6)
F(7)=f(1)+f(7)
F(8)=f(1)+f(2)+f(4)+f(8)
那么:
f(1)=F(1)
f(2)=F(2)-f(1)=F(2)-F(1)
f(3) =F(3)-F(1)
f(4)=F(4) -f(2)- f(1) =F(4)-F(2)
f(5) =F(5)-F(1)
f(6)=F(6)-F(3)-F(2)+F(1)
f(7)=F(7)-F(1)
f(8)=F(8)-F(4)
从中,可以看出,若n=p2(p为质数)那么,F(p)=f(1)+f(p),F(n)=f(1)+f(p)+f(p2),所以,f(n)=F(p2)-F(p)

AC代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long LL;
LL sum[MAX],num[MAX];
LL km(LL a,LL b){
    LL ans = 1;
    while(b){
        if(b & 1) ans = (ans * a) % mod;
        b /= 2; a = (a * a) % mod;
    }
    return ans;
}
int main()
{
    int T,nl = 0,n,x;
    scanf("%d",&T);
    while(T--){
        memset(num,0,sizeof(num));
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
            scanf("%d",&x),num[x]++;
        for(int i = 2; i < MAX; i++) num[i] += num[i - 1];
        LL ans = 0,cut = 0;
        for(int i = 2; i < MAX; i++){
            if(num[i - 1]) continue; // 有不满足的点
            sum[i] = 1;
            for(int j = i; j < MAX; j += i){
                if(j + i >= MAX) cut = num[MAX - 1] - num[j - 1]; // j ~ MAX
                else cut = num[j + i - 1] - num[j - 1];
                if(!cut) continue;
                sum[i] = (sum[i] * km(j / i,cut)) % mod;
            }
        }
        for(int i = MAX - 1; i >= 2; i--)
            for(int j = i + i; j < MAX; j += i)
                sum[i] = (sum[i] - sum[j] + mod) % mod;
        for(int i = 2; i < MAX; i++) ans = (ans + sum[i]) % mod;
        printf("Case #%d: %lld\n",++nl,ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值