SPOJ MSKYCODE - Sky Code

莫比烏斯&篩法

題目鏈接 Sky Code

題意:

給出一個大小為n的集合,求出滿足條件的4元子集的個數(shù)。
這個條件就是:該四元子集{a,b,c,d} ,滿足 gcd(a,b,c,d)=1

思路

第一步算法分析:
?第一感覺就是容斥原理,原命題等價:
?記Ai為gcd(a,b,c,d)=i的集合個數(shù)
?ans=C(n,4)-A2-A3-A5...+(A2x3+A2x5+...)-(A2x3x5...)
?再一看數(shù)據(jù)量:1e4 bingo,很小,果斷mobius打表直接做了。
?最后mobius下的公式就是:
?ans=∑mobius[i]*Ai
第二步復雜度分析:
?1e4 下mobius打表忽略不計
?求Ai 這個是比較常見的思路分兩步走
?1.求出是2,3,4,6,7...的倍數(shù)的集合元素個數(shù)Si
?那么Ai=C ( Si , 4 ).
?這個求法有兩種思路
?暴力求解

for(int i=0;i<n;i++){
    for(int j=0;j<=data[i];i++){
        if(data[i]%j==0) num[j]++;
    }
}

?復雜度 O(n * max(data[i])) 對于這里也就1e4 * 1e4估計還是可以的,但正式比賽肯定不行。
?篩法

        
        for(int i=2;i<=Max;i++){
            for(int j=i;j<=Max;j+=i){
                if(vis[j]) num[i]++;
            }
        }
        

?復雜度 Maxlg(Max) 這個一般就可以了,還有一種 sqrt(Max)lg(Max)的,我也不是很會寫 ,有興趣的可以加我qq討論下 844704...
?2.最后求下c(a,i) 就可以了
?這個也要預處理 c(n,m)=c(n-1,m-1)+c(n-1,m)
剪枝思考&&debug
?這個步驟很重要,也是區(qū)別1次AC和5次AC的分水嶺
?給出幾個簡單的剪枝
?n<4 直接為0
?Max壓縮篩法數(shù)據(jù)范圍
?debug 核心在于構(gòu)造邊界數(shù)據(jù),這點我也是菜鳥
?分享幾個自己當是用到的:
?c(10000,4)<51e14 longlong ok
?1e4
*ln(1e4)< 1e9(一般算法大于這個基本是出不來的....)

下面給出AC代碼:

//
//  main.cpp
//  MSKYCODE - Sky Code
//
//  Created by ccccsober on 6/25/16.
//  Copyright ? 2016 cccsober. All rights reserved.
//

#include <iostream>
#include <algorithm>
#include <cassert>
#include <string>
#include <sstream>
#include <math.h>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
#include <queue>
#include <deque>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <cctype>
#include <complex>
using namespace std;
const int MAX1= 20000  ;
//const int Mod=   ;
const double plus=0.49999999;
const int INF=0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
#define M_PI 3.141592653589
int num[MAX1];
LL multi[MAX1][5];
int mobius[MAX1];
int data[MAX1];
bool vis[MAX1];
vector<int> prime;
void Multi(){
    for(int i=0;i<MAX1;i++)
        multi[i][0]=1;
    for(int i=1;i<MAX1;i++){
        for(int j=1;j<5;j++){
            multi[i][j]=multi[i-1][j-1]+multi[i-1][j];
        }
    }
}
void Mobius(){
    memset(vis,0,sizeof(vis));
    mobius[1]=1;
    for(int i=2;i<MAX1;i++){
        if(!vis[i]){
            prime.push_back(i);
            mobius[i]=-1;
        }
        for(int j=0;j<prime.size() && i*prime[j]<MAX1;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j])
                mobius[i*prime[j]]=-mobius[i];
            else{
                mobius[i*prime[j]]=0;
                break;
            }
        }
    }
    
}
void _init(){
    memset(num,0,sizeof(num));
    memset(vis,0,sizeof(vis));
}
int main(int argc, const char * argv[]) {
    freopen("/Users/sperc4/Desktop/input.txt", "r", stdin);
    int n;
    Mobius();
    Multi();
    while(scanf("%d",&n)!=EOF){
        _init();
        int Max=-INF;
        int t;
        for(int i=0;i<n;i++){
            scanf("%d",&t);
            Max=max(Max,t);
            vis[t]=1;
//            for(int j=0;prime[j]<=t;j++){           //這個位置RT了一發(fā)
//                if(t%prime[j]) continue;
//                num[prime[j]]++;
//            }
        }
        if(n<4) {cout<<"0"<<endl;continue;}
        
        for(int i=2;i<=Max;i++){
            for(int j=i;j<=Max;j+=i){
                if(vis[j]) num[i]++;
            }
        }
        
        
        
        LL ans=0;
        for(int i=1;i<=Max;i++){
            ans+=(LL)mobius[i]*multi[num[i]][4];
        }
        printf("%lld\n",multi[n][4]+ans);
    }
    return 0;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容