HDU4675-GCD of Sequence(数论+组合计数)

博客详细介绍了如何解决HDU4675竞赛题目,该题涉及数论和组合计数的知识,主要探讨如何计算序列中的最大公约数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GCD of Sequence

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 949    Accepted Submission(s): 284


Problem Description
Alice is playing a game with Bob.
Alice shows N integers a1, a2, …, aN, and M, K. She says each integers 1 ≤ ai ≤ M.
And now Alice wants to ask for each d = 1 to M, how many different sequences b1, b2, …, bN. which satisfies :
1. For each i = 1…N, 1 ≤ b[i] ≤ M
2. gcd(b1, b2, …, bN) = 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(x1, x2, …, xn) is the greatest common divisor of x1, x2, …, xn
 

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: a1, a2, ..., an (1 ≤ ai ≤ 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
 
题意:有n个数(n <= 10^5),每个数的小于等于M(M <=10^5)
现在让你求把原数列改变k个数,使新数列最大公约数为d (d为1~M)的方法数,分别输出
思路:这是一道非常不错的数论题,即使推出公式,一些细节上的优化如果做得不到位的话也会超时。
当最大公约数为d时,易得1~M中d的倍数有 M/d个,我们统计原数列中d的倍数的个数cnt,那么所先要将原数列中的非d得倍数的数变成d的倍数,这样的方案数一共有
(M/d)^(n-cnt) 然后还剩下k-(n-cnt) 个数需要变换,那么方案数为 C(cnt))(n-k)*(M/d-1)^(k-(n-cnt) )_ 那么总的为d的倍数的方案数为(M/d)^(n-cnt)*C(cnt))(n-k)*(M/d-1)^(k-(n-cnt) ) 此时,还需要减掉为i*d的倍数的方案数(因为最终答案要求的是最大公约数为d的方案数,而此时的算的是为d的倍数的方案数)
这时候先算好组合数,(因为组合数的第二个数是不变的,复杂度从M降为1)并统计好1~M每个数出现的次数(复杂度从n降为lgm),从m开始计算答案,这样复杂度为m*lgm。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 300000+10;
const int MOD = 1000000007;
typedef long long ll;
int n,m,k;
int num[maxn];
ll Cot[maxn];
ll cnt[maxn];
ll ans[maxn];
inline void gcd(int a,int b,int& d,int &x,int &y){
    if(!b){
        d = a; x = 1;y = 0;
    }else{
        gcd(b,a%b,d,y,x);
        y -= x*(a/b);
    }
}
inline ll inv(int a){
    int d,x,y;
    gcd(a,MOD,d,x,y);
    return d == 1? (x+MOD)%MOD:-1;
}

inline ll pow_mod(int a,int b){
    if(b < 0) return 0;
    if(a==1) return 1;
    ll ret = 1,pow = a;
    while(b){
        if(b&1) ret = (ret*pow)%MOD;
        pow = (pow*pow)%MOD;
        b >>= 1;
    }
    return ret;
}

int main(){

    while(~scanf("%d%d%d",&n,&m,&k)){
        ll ret = 0;
        bool flag = true;
        memset(cnt,0,sizeof cnt);
        Cot[n-k] = 1;
        int tmp = n-k;
        for(int i = n-k+1; i <= n; i++){
            Cot[i] = (Cot[i-1]*i)%MOD*inv(i-tmp)%MOD;
        }
        for(int i = 1; i <= n; i++){
            scanf("%d",&num[i]);
            cnt[num[i]]++;
        }
        for(int i = m; i >= 1; i--){
            int sum = cnt[i],che = 0;
            for(int j = i+i; j <= m; j += i){
                sum += cnt[j];
                che = (ans[j]+che)%MOD;
            }
            if(sum < tmp) ans[i] = 0;
            else{
                int d = m/i;
                ans[i] = Cot[sum]*pow_mod(d-1,sum-tmp)%MOD*pow_mod(d,n-sum)%MOD;
                ans[i] = (ans[i]-che+MOD)%MOD;
            }
        }
        printf("%I64d",ans[1]);
        for(int i = 2; i <= m; i++){
            printf(" %I64d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值