hdu 5780 gcd

http://acm.hdu.edu.cn/showproblem.php?pid=5780

題意:

給定 x,n,求  \sum_{a=1}^n\sum_{b=1}^n\gcd(x^a-1,x^b-1)

假的解法:

一眼看起來,不太可做,按照常見套路,先輾轉相除一下,化簡得 \sum_{a=1}^n\sum_{b=1}^nx^{gcd(a,b)}-1 (我也不會化簡,建議baidu.com)

看起來莫比烏斯反演傻逼題,先化簡一下

F(d)=\sum_{d|n}f(n)

f(d)=\sum_{d|n}\mu(\frac{n}{d})F(n)

f(d)=\sum_{d|n}\mu(\frac{n}{d})*\left \lfloor \frac{N}{n} \right \rfloor*\left \lfloor \frac{N}{n} \right \rfloor*x^d

ans=\sum_{d=1}^n\sum_{d|n}\mu(\frac{n}{d})*\left \lfloor \frac{N}{n} \right \rfloor*\left \lfloor \frac{N}{n} \right \rfloor*x^d-n^2

ans=\sum_{T=1}^n*\left \lfloor \frac{N}{n} \right \rfloor*\left \lfloor \frac{N}{n} \right \rfloor*\sum_{k|T}\mu(k)*x^{\frac{T}{k}}-n^2

顯然第二個求和符號後面是一個迪利克雷卷積

f(k)=\mu(k),g(k)=x^k,h=(f*g) (迪利克雷卷積)

ans=\sum_{T=1}^n*\left \lfloor \frac{N}{n} \right \rfloor*\left \lfloor \frac{N}{n} \right \rfloor*h(T)-n^2

複雜度 O(Tnlogn)

怎麼感覺會超時?不管了,說不定他有保證 \sum\ n 在一定範圍內呢,先試試,然後就 TLE 到爆炸

真的題解:

於是看了一下網上的題解,反向枚舉公因數,然後通過歐拉函數來實現求出 n 個數中任選兩個互質的數字的方案數

\sum_{a=1}^n\sum_{b=1}^nx^{gcd(a,b)}-1

=\sum_{g=1}^{n}x^g\sum_{i=1}^{n/g}\sum_{j=1}^{n/g}[\gcd(a,b)=1]-n^2 (反向枚舉公因數)

=\sum_{g=1}^{n}x^g*(2*\sum_{i=1}^{n/g}\phi(i)-1)-n^2  (減去的一是因爲 gcd(1,1) = 1 ,被重複算了兩次)

然後考慮數論分塊,並用等比數列求和公式算次方就可以AC了(並不,還要判公比爲 1 )

莫比烏斯反演代碼:(TLE)

#include <bits/stdc++.h>
#define ll long long //T了就換int 試試
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 1e6 + 5;//用板子前先改範圍

bool check[MAXN];//值爲 false 表示素數,值爲 true 表示非素數
//int phi[MAXN];//歐拉函數表
int prime[MAXN];//連續素數表
int mu[MAXN + 10];//莫比烏斯函數
int tot;//素數的個數(從0開始
ll sub[MAXN];

const ll mod = 1e9 + 7;
ll power(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
ll fang[MAXN];

void jzk()
{
    //memset(check, false, sizeof(check));
    //phi[1] = 1;
    mu[1] = 1;
    tot = 0;
    for (int i = 2; i < MAXN; i++)
    {
        if (!check[i])
        {
            prime[tot++] = i;
            //phi[i] = i - 1;
            mu[i] = -1;
        }
        for (int j = 0; j < tot; j++)
        {
            if (i * prime[j] >= MAXN)
                break;
            check[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                //phi[i * prime[j]] = phi[i] * prime[j];
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                //phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
int main()
{
    jzk();
    int T;
    sc("%d", &T);
    while (T--)
    {
        ll x, n;
        sc("%lld%lld", &x, &n);
        fang[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            fang[i] = fang[i - 1] * x % mod; 
            sub[i] = 0;
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; i * j <= n; j++)
            {
                sub[i * j] = (sub[i * j] + mu[i] * fang[j]) % mod;
            }
        }
        for (int i = 1; i <= n; i++)
            sub[i] = (sub[i] + sub[i - 1]) % mod;
        ll ans = 0, j;
        for (int i = 1; i <= n; i = j + 1)
        {
            j = n / (n / i);
            ans = (ans + (n / j) * (n / j) % mod * (sub[j] - sub[i - 1] + mod)) % mod;
        }
        ans = (ans - n * n % mod + mod) % mod;
        pr("%lld\n", ans);
    }
}

歐拉函數代碼:(AC)

#include <bits/stdc++.h>
#define ll long long //T了就換int 試試
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 1e6 + 5;//用板子前先改範圍

bool check[MAXN];//值爲 false 表示素數,值爲 true 表示非素數
int phi[MAXN];//歐拉函數表
int prime[MAXN];//連續素數表
int mu[MAXN + 10];//莫比烏斯函數
int tot;//素數的個數(從0開始
ll sub[MAXN];

const ll mod = 1e9 + 7;

void jzk()
{
    //memset(check, false, sizeof(check));
    phi[1] = 1;
    mu[1] = 1;
    tot = 0;
    for (int i = 2; i < MAXN; i++)
    {
        if (!check[i])
        {
            prime[tot++] = i;
            phi[i] = i - 1;
            mu[i] = -1;
        }
        for (int j = 0; j < tot; j++)
        {
            if (i * prime[j] >= MAXN)
                break;
            check[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    for (int i = 1; i < MAXN; i++)
        sub[i] = (phi[i] + sub[i - 1]) % mod;
}
ll power(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
ll calc(ll k, ll a, ll b)
{
    if (k == 1)
        return b - a;
    ll ans1 = (power(k, a + 1) - k + mod) * power(k - 1, mod - 2) % mod;
    ll ans2 = (power(k, b + 1) - k + mod) * power(k - 1, mod - 2) % mod;
    return (ans2 - ans1 + mod) % mod;
}
int main()
{
    jzk();
    int T;
    sc("%d", &T);
    while (T--)
    {
        ll x, n;
        sc("%lld%lld", &x, &n);
        ll ans = -n * n % mod + mod, j;
        for (int i = 1; i <= n; i = j + 1)
        {
            j = n / (n / i);
            ans = (ans + (2 * sub[n / i] - 1 + mod) * (calc(x, i - 1 , j) + mod)) % mod;
        }

        //for (ll g = 1; g <= n; g++)
            //ans = (ans + fang[g] * (2LL * sub[n / g] - 1) + mod) % mod;
        pr("%lld\n", ans);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章