HDU 4675 GCD of Sequence (组合数学)

Description:

Alice is playing a game with Bob.
Alice shows N integers a 1 , a 2 , , a N a_{1}, a_{2}, …, a_{N} , and M , K M, K . She says each integers 1 a i M 1 ≤ a_{i} ≤ M .
And now Alice wants to ask for each d = 1 d = 1 to M M , how many different sequences b 1 , b 2 , , b N b_{1}, b_{2}, …, b_{N} . which satisfies :

  1. For each i = 1 N , 1 b i M i = 1…N, 1 ≤ b_{i} ≤ M
  2. g c d ( b 1 , b 2 , , b N ) = d gcd(b_{1}, b_{2}, …, b_{N}) = d
  3. There will be exactly K K position i i that a i ! = b i ( 1 i n ) a_{i} != b_{i} (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: g c d ( x 1 , x 2 , , x n ) gcd(x_{1}, x_{2}, …, x_{n}) is the greatest common divisor of x 1 , x 2 , , x n 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 ) 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 ) a_{1}, a_{2}, …, a_{N} (1 ≤ a_{i} ≤ M) which is original sequence.

Output

For each test contains 1 1 lines :
The line contains M M integer, the i t h i-th integer is the answer shows above when d d is the i t h 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 {a} , so b {b} can’t be (3, 3, 3), so Answer = 0

题意:

给一个序列 { a n } a i < = M ) \{a_{n}\}(a_{i} <= M) ,问有多少种 { b n } b i < = M ) \{b_{n}\}(b_{i} <= M) ,满足恰好 k k 个位置与 { a n } \{a_{n}\} 不同,且 g c d ( b 1 . . . b n ) = d 1 < = d < = M gcd (b_{1}...b_{n}) = d(1<= d <=M)

a n s [ d ] ans[d] 表示 g c d = d gcd = d 的满足条件的序列的个数,

F [ d ] F[d] 表示满足条件的 g c d = k d ( 1 < = k ) gcd = k*d (1<=k) 的序列的个数。那么很明显 当长度为 d d a n s [ d ] = F [ d ] a n s [ 2 d ] a n s [ 3 d ] . . . ans[d]= F[d] - ans[2*d] -ans[3*d]...

先求 a n s [ d ] ans[d] , 枚举gcd,在原来的序列中找到 g c d gcd 倍数的个数,设为 s u m sum . 因为题目要求恰好与 k k 个不同 而且 g c d = d gcd = d

那么新的序列肯定都得是 d d 的倍数,
原序列中不是d的倍数的个数为 n s u m n-sum .这些数肯定需要修改的,如果

  • n s u m > k n-sum>k 那么肯定是不可能的了。

  • n s u m < = k n-sum<=k 那么这 n s u m n-sum 个数要换成 d d 的倍数,每个数有 m d \frac{m}{d} 种,

所有的就是 ( m d ) n s u m (\frac{m}{d})^{n-sum} ,剩下的 s u m sum 个数中还要选出 k ( n s u m ) k-(n-sum) 个数变成不等于他们自己本身的 d d 的倍数,

每个数有 m d 1 \frac{m}{d}-1 种,那么这种情况一共有

C s u m k ( n s u m ) ( m / d 1 ) k ( n s u m ) C_{sum}^{k-(n-sum)}*(m/d-1)^{k-(n-sum)} 种。

F [ d ] = ( m d ) n s u m C s u m k ( n s u m ) ( m / d 1 ) k ( n s u m ) % m o d F[d]=(\frac{m}{d})^{n-sum}*C_{sum}^{k-(n-sum)}*(m/d-1)^{k-(n-sum)}\%mod

关于组合数打表求法可以参考这篇博客:点击这里

A C AC代码

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <stack>
#include <queue>
using namespace std;
#define sd(n) scanf("%d", &n)
#define sdd(n, m) scanf("%d%d", &n, &m)
#define sddd(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define pd(n) printf("%d\n", n)
#define pc(n) printf("%c", n)
#define pdd(n, m) printf("%d %d", n, m)
#define pld(n) printf("%lld\n", n)
#define pldd(n, m) printf("%lld %lld\n", n, m)
#define sld(n) scanf("%lld", &n)
#define sldd(n, m) scanf("%lld%lld", &n, &m)
#define slddd(n, m, k) scanf("%lld%lld%lld", &n, &m, &k)
#define sf(n) scanf("%lf", &n)
#define sc(n) scanf("%c", &n)
#define sff(n, m) scanf("%lf%lf", &n, &m)
#define sfff(n, m, k) scanf("%lf%lf%lf", &n, &m, &k)
#define ss(str) scanf("%s", str)
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define mem(a, n) memset(a, n, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define mod(x) ((x) % MOD)
#define gcd(a, b) __gcd(a, b)
#define lowbit(x) (x & -x)
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
const int inf = 0x3f3f3f3f;
inline int read()
{
    int ret = 0, sgn = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            sgn = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * sgn;
}
inline void Out(int a) //Êä³öÍâ¹Ò
{
    if (a > 9)
        Out(a / 10);
    putchar(a % 10 + '0');
}

ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b)
{
    return a * b / gcd(a, b);
}
///快速幂m^k%mod
ll qpow(ll a, ll b, ll mod)
{
    if (a >= mod)
        a = a % mod + mod;
    ll ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans = ans * a;
            if (ans >= mod)
                ans = ans % mod + mod;
        }
        a *= a;
        if (a >= mod)
            a = a % mod + mod;
        b >>= 1;
    }
    return ans;
}

// 快速幂求逆元
int Fermat(int a, int p) //费马求a关于b的逆元
{
    return qpow(a, p - 2, p);
}

///扩展欧几里得
int exgcd(int a, int b, int &x, int &y)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int g = exgcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - a / b * y;
    return g;
}

///使用ecgcd求a的逆元x
int mod_reverse(int a, int p)
{
    int d, x, y;
    d = exgcd(a, p, x, y);
    if (d == 1)
        return (x % p + p) % p;
    else
        return -1;
}

///中国剩余定理模板0
ll china(int a[], int b[], int n) //a[]为除数,b[]为余数
{
    int M = 1, y, x = 0;
    for (int i = 0; i < n; ++i) //算出它们累乘的结果
        M *= a[i];
    for (int i = 0; i < n; ++i)
    {
        int w = M / a[i];
        int tx = 0;
        int t = exgcd(w, a[i], tx, y); //计算逆元
        x = (x + w * (b[i] / t) * x) % M;
    }
    return (x + M) % M;
}

int t;
int n, m, k;
int res, temp;
const int N = 300000 + 5;
int a[N];
ll ans[N], cnt[N];
ll sum;
int F[N], Finv[N], inv[N]; //F是阶乘,Finv是逆元的阶乘

void init()
{
    inv[1] = 1;
    for (int i = 2; i < N; i++)
    {
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
    } //递推求逆元
    F[0] = Finv[0] = 1;
    for (int i = 1; i < N; i++)
    {
        F[i] = F[i - 1] * 1ll * i % MOD;
        Finv[i] = Finv[i - 1] * 1ll * inv[i] % MOD;
    }
}

int comb(int n, int m) //comb(n, m)就是C(n, m)
{
    if (m < 0 || m > n)
        return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}

int main()
{
    init();
    while (~sddd(n, m, k))
    {
        mem(cnt, 0);
        rep(i, 1, n)
        {
            sd(a[i]);
            cnt[a[i]]++;
        }
        for (int d = m; d >= 1; d--)
        {
            sum = 0;
            for (int i = d; i <= m; i += d)
                sum += cnt[i];
            if (sum < n - k)
                ans[d] = 0;
            else
            {
                ans[d] = qpow(m / d, n - sum, MOD) * qpow(m / d - 1, k - n + sum, MOD) % MOD;
                ans[d] = ans[d] * comb(sum, k - n + sum) % MOD;
                for (int i = d + d; i <= m; i += d)
                    ans[d] = (ans[d] - ans[i]) % MOD;
                ans[d] = (ans[d] + MOD) % MOD;
            }
        }
        rep(i, 1, m - 1)
            printf("%lld ", ans[i]);
        printf("%lld\n", ans[m]);
    }
    return 0;
}

发布了611 篇原创文章 · 获赞 390 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/qq_43627087/article/details/104006317
今日推荐