HDU 2017 多校聯合訓練賽2 1009 6053 TrickGCD 莫比烏斯函式
阿新 • • 發佈:2019-01-01
莫比烏斯函式完整定義的通俗表達是:
1)莫比烏斯函式μ(n)的定義域是N
2)μ(1)=1
3)當n存在平方因子時,μ(n)=0
4)當n是素數或奇數個不同素數之積時,μ(n)=-1
5)當n是偶數個不同素數之積時,μ(n)=1
定義域為 [ 1, 50 ] 的莫比烏斯函式值如下:(莫比烏斯函式相關內容摘自百度百科)
根據莫比烏斯函式的定義我們可以得到,所有素數的函式值μ(n)=-1,我們可以將所有以素數為公因子的B數列的所有情況加到ans集合中,然後我們會發現公因子為非素數的情況是重複出現的。
比如,6=2*3,6在公因子為2,3時都被ans++,因此要把6減掉一次(奇加偶減);
再比如,30=2*3*5,根據奇加偶減,因數為30的排列組合結果應該加上一次,具體分析一下,30可以分解為2*3*5,因子為2,3,5時ans++,因子為2*3=6,2*5=10,3*5=15時ans--,因此因子為30時ans++(奇加偶減)。
再再比如,210=2*3*5*7,因子為2,3,5,7時ans++四次,因子為2*3=6,2*5=10,2*7=14,3*5=15,3*7=21,5*7=35時ans--六次(其實就是組合數C(4,2)=6),因子為2*3*5=30,2*3*7=42,2*5*7=70,3*5*7=105時ans++四次(即C(4,3)=4),綜上ans被加了兩次,所以因子為210時ans--。
具體容斥原理使用莫比烏斯函式實現的過程請參考程式碼。
大體的解題思路有了,觀察資料大小我們發現遍歷因子,遍歷A數列兩個for迴圈100000*100000肯定超時,所以我們在進行一步優化。
假設公因子為5,那麼比10,11,12,13,14小的5的倍數都只有兩個,5和10,那麼,我們是不是能把10~14都看做10使用呢?這樣就可以將A數列分塊操作,使用快速冪取模進行優化。
最後是我在程式碼中遇到的一些問題。
1.在最後第九十多行的兩個for迴圈中要將i,j都轉化為long long,因為迴圈裡面的運算有用到i,j,用int型別會導致精度丟失
2.num[ ]陣列要開到200000,題目資料只有100000,但是在94行的for迴圈中,雖然j*i<=100000,因為mina最大是100000,所以(j+1)*i最大也是可能超過100000的,最大能達到200000。
3.快速冪取模long long powermod(long long a,long long b),引數和返回值都要取long long,否則會精度丟失。
程式碼
根據莫比烏斯函式的定義我們可以得到,所有素數的函式值μ(n)=-1,我們可以將所有以素數為公因子的B數列的所有情況加到ans集合中,然後我們會發現公因子為非素數的情況是重複出現的。
比如,6=2*3,6在公因子為2,3時都被ans++,因此要把6減掉一次(奇加偶減);
再比如,30=2*3*5,根據奇加偶減,因數為30的排列組合結果應該加上一次,具體分析一下,30可以分解為2*3*5,因子為2,3,5時ans++,因子為2*3=6,2*5=10,3*5=15時ans--,因此因子為30時ans++(奇加偶減)。
再再比如,210=2*3*5*7,因子為2,3,5,7時ans++四次,因子為2*3=6,2*5=10,2*7=14,3*5=15,3*7=21,5*7=35時ans--六次(其實就是組合數C(4,2)=6),因子為2*3*5=30,2*3*7=42,2*5*7=70,3*5*7=105時ans++四次(即C(4,3)=4),綜上ans被加了兩次,所以因子為210時ans--。
具體容斥原理使用莫比烏斯函式實現的過程請參考程式碼。
大體的解題思路有了,觀察資料大小我們發現遍歷因子,遍歷A數列兩個for迴圈100000*100000肯定超時,所以我們在進行一步優化。
假設公因子為5,那麼比10,11,12,13,14小的5的倍數都只有兩個,5和10,那麼,我們是不是能把10~14都看做10使用呢?這樣就可以將A數列分塊操作,使用快速冪取模進行優化。
最後是我在程式碼中遇到的一些問題。
1.在最後第九十多行的兩個for迴圈中要將i,j都轉化為long long,因為迴圈裡面的運算有用到i,j,用int型別會導致精度丟失
2.num[ ]陣列要開到200000,題目資料只有100000,但是在94行的for迴圈中,雖然j*i<=100000,因為mina最大是100000,所以(j+1)*i最大也是可能超過100000的,最大能達到200000。
3.快速冪取模long long powermod(long long a,long long b),引數和返回值都要取long long,否則會精度丟失。
程式碼
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int mod = 1e9+7; int cas, n; int mina; int mo[100005];//mobius函式打表 int num[200005];//數值為i的數的個數 int a[100005]; /*long long powermod(ll a, ll b) { long long ans=1; while(b) { if(b%2==1)ans=ans*a%mod; a=(a*a)%mod; b/=2; } printf ("ans=========%d\n",ans); return ans; }*/ long long int PowerMod (long long a,long long b) { long long ans = 1; a = a%mod; while (b > 0) { if (b%2 == 1) ans = (ans*a)%mod; b = b/2; a = (a*a)%mod; } //printf ("ans=========%d\n",ans); return ans; } void mobius(int mn) { mo[1] = 1; for(int i=1; i<=mn; i++) { for(int j=i+i; j<=mn; j+=i) { mo[j]-=mo[i]; } } } int main() { //freopen( "1009in.txt" , "r" , stdin ); //freopen( "1009out2.txt" , "w" , stdout ); cas = 0; mobius(100000); /*for (int i=1; i<=10000; i++) printf ("%d %d \n",i,-mo[i]); printf("\n");*/ int T; scanf ("%d",&T); while (T--) { memset(num, 0, sizeof(num)); mina = 100005; scanf ("%d",&n); for (int i=1; i<=n; ++i) { scanf ("%d",&a[i]); if (mina > a[i]) { mina = a[i]; } } for (int i=1; i<=n; ++i) { num[a[i]] ++; } /*for (int i=0; i<=100000; i++) printf ("%d ",num[i]); printf ("\n");*/ for (int i=1; i<=200000; ++i) { num[i] += num[i-1]; //printf ("%d ",num[i]); } //printf ("\n"); long long sum = 0; //printf ("mina================%d\n",mina); for (long long int i=2; i<=mina; ++i)//遍歷因數 { long long gg = 1; for (long long j=1; j*i<=100000; ++j)//遍歷a序列中的所有數 { gg = (gg*PowerMod(j, num[(j+1)*i-1]-num[j*i-1]))%mod;//假設i=5,求5~9,num[9]-num[5-1] } //printf ("%lld\n",mo[i]*gg); sum = (sum-gg*mo[i]%mod+mod)%mod; //printf ("s--%lld\n",sum); } printf ("Case #%d: %lld\n",++cas,sum); } return 0; }