HDU 4675 GCD of Sequence(数论+组合数学||莫比乌斯反演)

GCD of Sequence

 Alice is playing a game with Bob.
Alice shows N integers a 1, a 2, …, a N, and M, K. She says each integers 1 ≤ a i ≤ M.
And now Alice wants to ask for each d = 1 to M, how many different sequences b 1, b 2, …, b N. which satisfies :
1. For each i = 1…N, 1 ≤ b[i] ≤ M
2. gcd(b 1, b 2, …, b N) = d
3. There will be exactly K position i that ai != bi (1 ≤ i ≤ n)

Alice thinks that the answer will be too large. In order not to annoy Bob, she only wants to know the answer modulo 1000000007.Bob can not solve the problem. Now he asks you for HELP!
Notes: gcd(x 1, x 2, …, x n) is the greatest common divisor of x 1, x 2, …, x n 

Input
The input contains several test cases, terminated by EOF.
The first line of each test contains three integers N, M, K. (1 ≤ N, M ≤ 300000, 1 ≤ K ≤ N)
The second line contains N integers: a 1, a 2, …, a n (1 ≤ a i ≤ M) which is original sequence.

Output
For each test contains 1 lines :
The line contains M integer, the i-th integer is the answer shows above when d is the i-th number.
Sample Input

3 3 3
3 3 3
3 5 3
1 2 3

Sample Output

7 1 0
59 3 0 1 1

Hint

In the first test case :
when d = 1, {b} can be :
(1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 2, 2)
(2, 1, 1)
(2, 1, 2)
(2, 2, 1)
when d = 2, {b} can be :
(2, 2, 2)
And because {b} must have exactly K number(s) different from {a}, so {b} can't be (3, 3, 3), so Answer = 0

题意:

给你一个数数组 a[i][1,m]a[] a [ i ] , 其 中 给 出 的 每 一 个 数 字 和 要 求 的 数 字 都 是 属 于 [ 1 , m ] , 现 在 问 你 根 据 a [ ] 构造一个 b[]abk b [ ] , 且 a 和 b 中 间 的 不 相 等 的 元 素 的 个 数 恰 好 有 k 个。

现在问你 gcdb[]1,2,,m g c d ( b [ ] ) 分 别 为 1 , 2 , … … , m 的 个 数 分 别 有 多 少 种 可 能 情 况

分析:

通过以往的做题经验我们知道,对于求一个范围内含有确定的因数 d d 的个数并不好求,但是好求的是一个范围内含有因数 dd d 及 其 倍 数 的 数 字 的 数 量 即 总 数 除 以 d

那么对于这道题我们思路可以从大到小枚举m假设枚举的数是 im i , 我 们 首 先 可 以 求 出 m 范围内有多少个数因子含有 iinum=mim i 及 其 i 的 倍 数 及 为 n u m = m i ( m 范围内只有这么多,这是全部)

那么实际给定的 a a 数列中有多少是满足这个条件的数呢?

我们输入时完全可以记录每个数的个数(因为 aii a 数 列 每 个 数 并 不 大 ) , 这 样 我 们 只 需 从 i 每 次 以 i 为步长统计出 aiicur a 数 列 中 的 含 有 i 及 其 i 的 倍 数 作 为 因 子 的 数 的 个 数 及 为 c u r

那么总数 na n 减 去 这 个 个 数 剩 下 的 就 是 a 数列中不满足条件的数的个数即 ncur n − c u r

这个时候我们就需要判断,因为我们只能确定的修改k个值,那么这时就分了三种情况

1) ncur>k n − c u r > k :
HDU 4675 GCD of Sequence(数论+组合数学||莫比乌斯反演)_第1张图片

说明剩下的需要修改的数大于 kiigcd=i k 个 才 能 保 证 每 个 数 含 有 因 子 是 为 i 及 其 i 的 倍 数 从 而 保 证 g c d = i ,而题目要求我们只能该k个那么在这种情况下将没有符合条件的 b b 数列,因而答案为0。

2) ncur=k n − c u r = k :
HDU 4675 GCD of Sequence(数论+组合数学||莫比乌斯反演)_第2张图片

此时说明剩下的数恰好k个那么我们直接修改即可了根据上面我们知道共有 num=mi n u m = m i 种选择,共有k个需要选因此共有 numncur=numk n u m n − c u r = n u m k

3) ncur<k n − c u r < k :

HDU 4675 GCD of Sequence(数论+组合数学||莫比乌斯反演)_第3张图片

说明剩下的 akk a 数 列 中 不 满 足 条 件 的 数 的 个 数 小 于 k 个 , 而 我 们 又 必 须 要 该 k 个,因此我们只能再从已将满足条件的数中再修改 cur+kn c u r + k − n 个,因此这 cur+kn c u r + k − n 需要从满足条件的 cur c u r 中选,此时便需要用到组合数 Ccur+kncur C c u r c u r + k − n 中,而每个我们可以替换的数是 numa n u m 中 除 去 a 中原来那个数本身,因此每个位置还有 num1 n u m − 1 种选择即 (num1)cur+kn ( n u m − 1 ) c u r + k − n ,所以这种情况下我们除了需要把 ncur n − c u r 个不满足条件的位置修改,该需要把 cur+kn c u r + k − n 个已经满足条件的修改以凑够修改k个数

因此答案为情况2和情况3的组合即为

numncur×Ccur+kncur×(num1)cur+kn n u m n − c u r × C c u r c u r + k − n × ( n u m − 1 ) c u r + k − n

综上我们就求得了 bgcdii b 数 组 的 g c d 为 i 即 i 的倍数的所有可能情况

我们当然不想要 gcdim g c d 为 i 的 倍 数 的 情 况 , 因 此 需 要 减 去 , 这 时 就 体 现 出 了 m 从大到小枚举的好处,我们发现当我们求 ii 得 i 是 i 的倍数的情况已经求出来了,因此我们只需要在遍历一遍i的倍数的答案之间减去即可。

code:

#include 
using namespace std;
const int maxn = 300003;
const int mod = 1e9+7;
typedef long long ll;
int a[maxn],f[maxn],n,m,k,ai,tep;
ll P[maxn],Q[maxn];

ll q_pow(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

ll C(ll x,ll y){
    if(y == 0 || x == y) return 1;
    ll ans = P[x] * Q[y] % mod;
    ans = ans * Q[x-y] % mod;
    return ans;
}

int main(){
    ll cur,tot,num;
    Q[1] = P[0] = 1;
    for(int i = 1; i < maxn; i++){
        P[i] = (P[i-1] * i) % mod;
        Q[i] = q_pow(P[i],mod-2);
    }
    while(scanf("%d%d%d",&n,&m,&k) != EOF){
        for(int i = 1; i <= m; i++) a[i] = f[i] = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d",&ai);
            a[ai]++;
        }
        for(int i = m; i > 0; i--){
            cur = 0;
            tot = 1;
            num = m / i;
            for(int j = i; j <= m; j += i) cur += a[j];
            if(n - cur > k){
                f[i] = 0;
                continue;
            }
            tot = q_pow(num,n-cur);
            if(n - cur != k){
                tep = C(cur,k+cur-n) * q_pow(num-1,k+cur-n) % mod;
                tot = tot * tep % mod;
            }
            f[i] = tot;
            for(int j = i + i; j <= m; j += i){
                f[i] -= f[j];
                if(f[i] < 0) f[i] += mod;
            }
        }
        printf("%d",f[1]);
        for(int i = 2; i <= m; i++){
            printf(" %d",f[i]);
        }
        printf("\n");
    }
    return 0;
}

实际上经过上面的分析我们发现,上面我们说的三种情况实际上都可以用第三种情况表示即

numncur×Ccur+kncur×(num1)cur+kn n u m n − c u r × C c u r c u r + k − n × ( n u m − 1 ) c u r + k − n

这个公式可以完整的表示三种情况

而且这是求的gcd = i的倍数的个数

由此我们想到了莫比乌斯反演

此时的

F(i)=numncur×Ccur+kncur×(num1)cur+kn F ( i ) = n u m n − c u r × C c u r c u r + k − n × ( n u m − 1 ) c u r + k − n

所以

f(x)=x|dμ(dx)F(d) f ( x ) = ∑ x | d μ ( d x ) ⋅ F ( d )

只要预处理 Fd 数 F ( d ) ,然后直接求和累加即可,代码就不再写了,只说一下莫比乌斯反演的思路

你可能感兴趣的:(组合数学,数论)