cf900 D. Unusual Sequences(容斥+dp+数论)

题意:

求gcd为 m 且和为 n 的不同数组的数量取模。

\(1\le n,m\le 1e9\)

思路:

若 n 不是 m 的倍数,则答案为0。

否则,把 n 看成 n/m 个 m 之和。由隔板法,和为 n 且元素均为 m 的倍数的不同数组有 \(2^{n/m+1}\) 个,记为 \(dp(1)\)

但是gcd为m就要求数组中的元素均是m的倍数,但不是2m、3m、4m等的倍数,所以要用容斥

一个数的因子总数不会很多,可以n^2 dp:

从大到小考虑n的所以形如km的因子,容斥更新dp

const int MOD = 1e9 + 7;
int n, m;

int qmi(int t, int k, int p=MOD)
{
    int res = 1;
    while (k)
    {
        if (k&1) res = (ll)res * t % p;
        t = (ll)t * t % p; k >>= 1;
    }
    return res;
}
void del(int &x, int y) { x-=y; if(x<0)x+=MOD; }

vector<int> ve;
void getMdivs(int n)
{
    for(int i = 1; i <= sqrt(n); i++)
        if(n % i == 0) //n的形如km的因子
        {
            if(i % m == 0) ve.pb(i);
            if(i != n/i && (n/i) % m == 0) ve.pb(n/i);
        }
}

main()
{
    cin >> m >> n;

    if(n % m) return puts("0"), 0;

    getMdivs(n);
    sort(all(ve));

    vector<int> dp(ve.size());
    for(int i = dp.size() - 1; i >= 0; i--)
    {
        dp[i] = qmi(2, n / ve[i] - 1);
        for(int j = i + 1; j < dp.size(); j++)
            if(ve[j] % ve[i] == 0) //容斥,减去倍数
                del(dp[i], dp[j]);
    }

    cout << dp[0];
}

posted @ 2022-03-07 00:14  Bellala  阅读(43)  评论(0)    收藏  举报