51nod 1616 最小集合( 數(shù)論 )

題目鏈接

題目大意

定義一個(gè)集合A昼榛,如果x和y屬于A,那么x和y的最大公約數(shù)也屬于A剔难。問題給出n個(gè)屬于集合A的數(shù)字胆屿,問集合A最少有多少個(gè)不同的數(shù)字奥喻。n<=105,集合A的數(shù)字均<=106莺掠。

解答

我的做法是衫嵌,開一個(gè)10^6(下記為M)的數(shù)組記錄集合A是否包含某個(gè)元素。從大到小枚舉彻秆,判斷一個(gè)最初不在集合A的數(shù)字i是否也屬于集合A楔绞。判斷的方式是找出所有在集合A的i的倍數(shù),選兩個(gè)數(shù)唇兑,看這兩個(gè)數(shù)的gcd是否為i酒朵。找出所有i的倍數(shù)的時(shí)間復(fù)雜度是O(M*log(M))的,不會(huì)超時(shí)扎附。但是判斷是否存在兩個(gè)數(shù)的gcd為i我能想到的做法就是容斥蔫耽。設(shè)i×j[1],i×j[2]...i×j[m]是i的屬于集合A的倍數(shù),把j[1],j[2],...,j[m]質(zhì)因數(shù)分解留夜,枚舉j[k] (1<=k<=m)匙铡,用容斥原理算出與j[k]不互質(zhì)的個(gè)數(shù)t,只要t<m碍粥,那么i的倍數(shù)中就存在兩個(gè)數(shù)的gcd是i鳖眼,i就是在集合A的數(shù)字。
質(zhì)因數(shù)分解需要先預(yù)處理好嚼摩,容斥的時(shí)候钦讳,我害怕超時(shí),盡量用上重復(fù)的信息(比如判斷i1和i2的時(shí)候枕面,它們都有相同倍數(shù)在同一集合愿卒,可以重復(fù)利用)。
這個(gè)做法理論上時(shí)間復(fù)雜度很大潮秘,每次容斥都是O(2^質(zhì)數(shù)個(gè)數(shù))琼开,但是由于實(shí)際上質(zhì)數(shù)的個(gè)數(shù)跟容斥的數(shù)有關(guān),所以大部分情況下不是很大枕荞,再加上只是需要判斷存在與否柜候,所以實(shí)際跑起來不會(huì)很慢,在比賽的時(shí)候過掉了所有的test买猖。
比賽的時(shí)候改橘,由于太久沒寫容斥了,寫add和que函數(shù)的時(shí)候都算了正負(fù)號(hào)玉控,計(jì)算的結(jié)果相當(dāng)于全部加了起來飞主。

xb由于不太滿意我的做法,后來想到了一個(gè)正確的做法,時(shí)間復(fù)雜度就是O(M*log(M))碌识。
這個(gè)集合有個(gè)關(guān)鍵性質(zhì)碾篡,給出的n個(gè)數(shù)的非空子集的gcd一定在集合內(nèi),并且不屬于給出n個(gè)數(shù)的數(shù)字一定是這n個(gè)數(shù)中的一個(gè)子集的gcd筏餐。我們可以用逆推的方法得到這個(gè)結(jié)論开泽。設(shè)給出n個(gè)數(shù)的集合為S,集合A為最終答案魁瞪。若x∈A穆律,x=gcd(a,b),a∈S而b?S导俘。那么可以讓b=gcd(c,d)這樣迭代替換下去峦耘,直到全部元素屬于S為止。
在上面性質(zhì)的前提下旅薄,xb有一個(gè)很強(qiáng)大的判斷辅髓,決定當(dāng)i?S時(shí),是否屬于A少梁。
設(shè)cnt[i]表示S集合中i的倍數(shù)的個(gè)數(shù)洛口。如果y是x的倍數(shù),那么顯然有cnt[x]>=cnt[y]凯沪,當(dāng)cnt[x]=cnt[y]時(shí)第焰,意味著x和y在S中的倍數(shù)是完全一樣的,由于y是x的倍數(shù)著洼,所以不可能在x和y的倍數(shù)中找到一個(gè)子集gcd是x樟遣,所以x?A而叼。
反過來身笤,如果x的所有倍數(shù)y,都有cnt[x]>cnt[y]葵陵,那么一定有x∈A液荸。這時(shí)求x的所有倍數(shù)的gcd,一定是x脱篙。
這個(gè)做法十分的強(qiáng)大娇钱,具體可以參看xb的題解

以下是我容斥暴力的代碼

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;

#define bll long long
#define For(i,a,b) for (int i=(a),_##i=(b); i<=_##i; i++)
#define Rof(i,a,b) for (int i=(a),_##i=(b); i>=_##i; i--)
#define Mem(a,b) memset(a,b,sizeof(a))

const int maxL=1000*1000+10;
int N,fp[maxL],Prime[maxL],M;
int nex[maxL],G[maxL];
bool boo[maxL];

void Preapre()
{
    Mem(fp,0);
    Prime[0]=0;
    For(i,2,maxL-1)
    {
        if (!fp[i]) fp[i]=i,Prime[++Prime[0]]=i;
        For(j,1,Prime[0])
        {
            if (i*Prime[j]>=maxL) break;
            fp[i*Prime[j]]=Prime[j];
            if (i%Prime[j]==0) break;
        }
    }
    nex[1]=1;
    For(i,2,maxL-1)
    {
        int x=i;
        int p=fp[i];
        while(x%p==0) x/=p;
        nex[i]=x;
    }
}

int gcd(int a,int b)
{
    if (b==0) return a;
    return gcd(b,a%b);
}

void add(int state,int x,int d)
{
    if (x==1) 
    {
        if (state>1) G[state]+=d;
        return ;
    }
    add(state*fp[x],nex[x],-d);
    add(state,nex[x],d);
}

int que(int state,int x)
{
    if (x==1) 
    {
        return G[state];
    }
    int ret=0;
    ret+=que(state*fp[x],nex[x]);
    ret+=que(state,nex[x]);
    return ret;
}

void Done()
{
    static bool vis[maxL];
    static int o[maxL];
    Mem(vis,0);
    Mem(G,0);
    Rof(i,M,1)
        if (!boo[i])
        {
            int rr=0;
            for (int j=2; j*i<=M; j++)
            {
                bool flag=boo[j*i];
                if (flag) o[++rr]=j;
                if (flag!=vis[j])
                {
                    add(1,j,(flag ? -1 : 1));
                    vis[j]=flag;                
                }
            }
            For(j,1,rr)
            {
                int ret=que(1,o[j]);
                if (ret<rr)
                {
                    boo[i]=1;
                    break;
                }
            }
        }
    int ans=0;
    For(i,1,M)
        ans+=boo[i];
    printf("%d\n",ans);
}

int main(int argc, char* argv[])
{
    Preapre();
    for (; scanf("%d",&N)!=EOF; )
    {
        Mem(boo,0);
        M=0;
        For(i,1,N)
        {
            int x; scanf("%d",&x);
            M=max(M,x);
            boo[x]=1;
        }
        Done();
    }
    return 0;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绊困,隨后出現(xiàn)的幾起案子文搂,更是在濱河造成了極大的恐慌,老刑警劉巖秤朗,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煤蹭,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)硝皂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門常挚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稽物,你說我怎么就攤上這事奄毡。” “怎么了贝或?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵吼过,是天一觀的道長。 經(jīng)常有香客問我咪奖,道長那先,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任赡艰,我火速辦了婚禮售淡,結(jié)果婚禮上慷垮,老公的妹妹穿的比我還像新娘揖闸。我一直安慰自己料身,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布芹血。 她就那樣靜靜地躺著贮泞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幔烛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天饿悬,我揣著相機(jī)與錄音,去河邊找鬼狡恬。 笑死珠叔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弟劲。 我是一名探鬼主播祷安,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼兔乞,長吁一口氣:“原來是場噩夢啊……” “哼汇鞭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虱咧,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤腕巡,失蹤者是張志新(化名)和其女友劉穎玄坦,沒想到半個(gè)月后绘沉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煎楣,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡车伞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年另玖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了困曙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谦去。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鳄哭,靈堂內(nèi)的尸體忽然破棺而出要糊,到底是詐尸還是另有隱情妆丘,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布勺拣,位于F島的核電站,受9級(jí)特大地震影響车柠,放射性物質(zhì)發(fā)生泄漏剔氏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一羊苟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜡励,春花似錦、人聲如沸凉倚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扮碧。三九已至,卻和暖如春慎王,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赖淤。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工谅河, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咱旱,地道東北人绷耍。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓莽龟,卻偏偏與公主長得像锨天,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子病袄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗(yàn)益缠。 張土汪:刷leetcod...
    土汪閱讀 12,748評(píng)論 0 33
  • thiele插值算法 1點(diǎn)插值算法 function [C,c]=thiele(X,Y,Z)%X為插值點(diǎn)橫坐標(biāo),Y...
    00crazy00閱讀 1,996評(píng)論 0 4
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 3,845評(píng)論 0 6
  • 你說月灑寒江幅慌,玉柱瓊梁宋欺; 后來冷鏡殘鉤胰伍,三更榻?jīng)觥?你說夢囈故園 齿诞,桃花水里游鴛鴦骂租; 后來千山暮雪,老翅 幾回自奔...
    巧克力吐司閱讀 263評(píng)論 0 0
  • 啟動(dòng)頁是一個(gè)應(yīng)用的入口渗饮,所以在該軟件進(jìn)入主界面之前應(yīng)該需要做些什么工作宿刮,是要很清楚的私蕾。 啟動(dòng)頁的功能: 1:延時(shí)跳...
    隰有荷閱讀 240評(píng)論 0 2