【bestcoder #39】CD題解

Code

Accepts: 125 Submissions: 736
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
問題描述
wld有一個長度爲n的序列a1..an
wld想要你給出下面這段c++代碼的輸出:
int calc()
{
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
res+=gcd(a[i],a[j])*(gcd(a[i],a[j])-1);
res%=10007;
}
return res;
}
保證1≤n≤10000,1≤ai≤10000 (對於任意1≤i≤n)
輸入描述
多組數據(最多10組)
對於每組數據:
第一行:一個數n表示序列的長度
第二行:n個數,依次爲a1,a2,…,an
輸出描述
對於每組數據:
輸出calc函數的返回值
輸入樣例
5
1 3 4 2 4
輸出樣例
64
Hint
gcd(i,j)爲兩數的最大公因數。

兩種做法,先說一種簡單的:
①首先對於每一個數字i ,都預處理出有多少個(sum[i] )給定得數包含他作爲因數,並將sum[i] 平方。

②我們要考慮的問題是如果兩個數都含有因數4,他們在2和4中都出現了,而只需要算4的這一次,爲了處理這種情況,我們從大到小枚舉因數:
最大的因數必然不會產生上述問題;
對於較小的數i ,枚舉他的倍數j ,我們讓sum[i]=sum[i]sum[j] (注意這裏都是平方),類似於容斥原理,就把不合題意的去掉了!

第二種做法用到了莫比烏斯反演:
如果是求gcd(i,j) ,我們可以利用n=φ(d)(dn) ,直接枚舉因數k ,每個k 貢獻φ(k)

注:
n=Σφ(d)(dn) 的證明(from zyf-zyf)。
n 個分數,分別是1n,2n,3n.....nn ,把他們化簡之後,每個分母一定是n 的因數,也就是說k 當分母的個數是φ(k) ,得證。

變成了求Σgcd(i,j)(gcd(i,j)1) 後可以繼續沿用上面的思想,之前是φ(d)(dn) ,我們假設現在變成了g(d)(dn) ,可以聯想到莫比烏斯反演。
假設G(n)=g(d)(dn) ,那麼根據剛纔的定義G(n)=n(n1) ,通過莫比烏斯反演,我們可以通過G[n] 來求出g(n)
g(n)=μ(nd)G(d)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <set>
#include <cmath>
#define mod 10007
#define LL long long
#define M 10000
#include <vector>
#define pb push_back
using namespace std;
vector<int> c[M+5];
int v[M+5],f[M],mu[M+5],tot,cnt[M+5],p[M],n;
void Getmobius()
{
    mu[1]=1;
    for (int i=2;i<=M;i++)
    {
        if (!v[i])
        {
            p[++tot]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=tot;j++)
        {
            if (i*p[j]>M) break;
            v[i*p[j]]=1;
            if (i%p[j]==0)
            {
                mu[i*p[j]]=0;
                break;
            }
            else mu[i*p[j]]=-mu[i];
        }
    }
}
void Prepare()
{
    for (int i=2;i<=M;i++)
    {
        int x=i;
        for (int j=1;j<=sqrt(i);j++)
            if (x%j==0)
            {
                c[i].pb(j);
                if (x/j!=j)
                    c[i].pb(x/j);
            }
    }
    for (int i=1;i<=M;i++)
        for (int j=0;j<c[i].size();j++)
            (f[i]+=mu[i/c[i][j]]*c[i][j]*(c[i][j]-1))%=mod;
}
int main()
{
    Getmobius();
    Prepare();
    while (scanf("%d",&n)!=EOF)
    {
        for (int i=1;i<=M;i++)
            cnt[i]=0;
        for (int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            for (int j=0;j<c[x].size();j++)
                cnt[c[x][j]]++;
        }
        int ans=0;
        for (int i=2;i<=M;i++)
            (ans+=cnt[i]*cnt[i]%mod*f[i])%=mod;
        printf("%d\n",(ans+mod)%mod);
    }
    return 0;
}

Lucky

Accepts: 34 Submissions: 267
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
問題描述
wld有n個數(a1…an)
保證對於任意1≤i≤n,1≤ai≤n
wld有一個常數k保證2≤k≤2∗n
爲了消除歧義保證k爲奇數
他有m個詢問
每個詢問有參數l1,r1,l2,r2
對於每個詢問你需要回答有多少個二元組(i,j)滿足:
l1≤i≤r1且l2≤j≤r2且ai+aj=k
保證1≤n≤30000,1≤m≤30000
輸入描述
多組數據(最多5組)
對於每組數據:
第一行:一個數n表示數的個數
接下來一行:一個數k表示wld的常數
接下來一行:n個數,依次爲a1,a2,…an
接下來一行:一個數m表示詢問數
接下來m行:四個數l1,r1,l2,r2表示這組詢問的參數
輸出描述
對於每組數據:
對於每個詢問輸出二元組的數目
輸入樣例
5
3
1 2 1 2 3
1
1 2 3 5
輸出樣例
2
Hint
a1 + a4 = 3
a2 + a3 = 3

莫隊或者分塊都可以。

莫隊:
這裏寫圖片描述
(類似於容斥原理)

分塊:
預處理出f[i][j][k] 表示第i 塊到第j 塊和爲k 的方案數,然後整塊O(1) 算,其他<n 的部分暴力算。

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