日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

莫比莫斯小结

發布時間:2023/12/13 综合教程 38 生活家
生活随笔 收集整理的這篇文章主要介紹了 莫比莫斯小结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前幾天多校的時候碰到一道莫比烏斯的題目,一臉懵逼,然后惡補了幾道。

先看下莫比烏斯的基本概念:

定理:和是定義在非負整數集合上的兩個函數,并且滿足條件,那么我們得到結論

在上面的公式中有一個函數,它的定義如下:

(1)若,那么

(2)若,均為互異素數,那么

(3)其它情況下

來自acdreamer大佬的博客。

先說下第一道入門題目(雖然入門,處理的東西也不少。。):

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

題目意思:對于1<=x<=d,1<=y<=d 求gcd(x,y)=k的個數

題解:首先第一步,對于對于1<=x<=d,1<=y<=d 如果 gcd(x,y)=k,那么對于1<=x/k<=d/k,1<=y/k<=d/k gcd(x,y)=1。問題就轉化為對于1<=x<=d/k,1<=y<=d/k 求gcd(x,y)=1的個數。

然后我們定義兩個函數。F(n)與f(n);

F(d)為 有多少對(x,y)滿足gcd(x,y)== d 的倍數 。

f(d)為有多少對(x,y)滿足gcd(x,y)== d 。

我們要求的是f(d)的值,但我們發現F(d)函數比較容易求解F(d)=(n/d)*(m/d);(這個怎么理解呢,我們把范圍1~n里面能夠整除x的數的個數表示為n/x;對于兩個數,如果兩個數的因子都有d,那么這兩個數的倍數的gcd一定是d的倍數)。

是不是可以套進去?

而且我們簡化之后,要求的n為1 那么u也就很好求了。

貼一個線性求mu的代碼:

int prime[100001];
int mu[100001];
int sum[100001];
void init()
{
    int vis[100001];
    memset(vis,0,sizeof(vis));
    memset(mu,0,sizeof(mu));
    memset(prime,0,sizeof(prime));
    mu[1]=1;
    int ret=0;
    for(int i=2;i<=100000;i++)
    {
        if(!vis[i])
        {
            prime[ret++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<ret && i*prime[j] < 100000;j++)
        {
            int temp=i*prime[j];
            vis[temp]=1;
            if(i%prime[j]) mu[temp]=-mu[i];
            else
            {
                mu[temp]=0;
                break;
            }
        
    }
}

有了這些之后,還有一個問題。題目要求去重,這個怎么理解呢?

此時,走到這一步,我們已經求得了(x,y)滿足gcd(x,y)=1的對數 ,但題目中說明了,(1,2)和(2,1)算一種情況,那么我們就要減去多余了的情況,怎那么找出那些多算進去的情況呢? 下面的圖畫的很清楚:

G(b,b)就是多算進去的這些情況,

那么 G(b,d)- G(b,b)/ 2 就是最終我們要求的結果了,至于這一點,有不懂的請在紙上畫一畫,這不是我要講的重點了。(轉自http://blog.csdn.net/lixuepeng_001/article/details/50577932)

ac代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;  
  
const int maxn=1e5+7;  
bool vis[maxn];  
int prime[maxn],mu[maxn];  
int cnt;  
  
void Init(){  
    int N=maxn;  
    memset(prime,0,sizeof(prime));  
    memset(mu,0,sizeof(mu));  
    memset(vis,0,sizeof(vis));  
    mu[1] = 1;  
    cnt = 0;  
    for(int i=2; i<N; i++){  
        if(!vis[i]){  
            prime[cnt++] = i;  
            mu[i] = -1;  
        }  
        for(int j=0; j<cnt&&i*prime[j]<N; j++){  
            vis[i*prime[j]] = 1;  
            if(i%prime[j]) mu[i*prime[j]] = -mu[i];  
            else{  
                mu[i*prime[j]] = 0;  
                break;  
            }  
        }  
    }  
}  
  
void getMu(){  
    int N=maxn;  
    for(int i=1;i<N;++i){  
        int target=i==1?1:0;  
        int delta=target-mu[i];  
        mu[i]=delta;  
        for(int j=2*i;j<N;j+=i)  
            mu[j]+=delta;  
    }  
}  
  
int main()  
{  
    ios::sync_with_stdio(false);  
    int a,b,c,d,k;  
    int T,Case=0;  
    Init();  
    cin>>T;  
    while(T--){  
        cin>>a>>b>>c>>d>>k;  
        cout<<"Case "<<++Case<<": ";  
        if(k==0){  
            cout<<"0"<<endl;  
            continue;  
        }  
        b/=k,d/=k;  
        long long ans1=0,ans2=0;  
        for(int i=1;i<=min(b,d);i++){  
            ans1+=(long long)mu[i]*(b/i)*(d/i);  
        }  
        for(int i=1;i<=min(b,d);i++){  
            ans2+=(long long)mu[i]*(min(b,d)/i)*(min(b,d)/i);  
        }  
        cout<<ans1-ans2/2<<endl;  
    }  
    return 0;  
}  

對于這個問題還有一個知識點可以,就是分塊求和。

對于[n/x]這個表達式,當[n/x]=d是不是有很多x可以對應這個值?

比如a'=100,那么d在[34,50]之間a'/d都是2。

那么我們可以把連續的一段d一起來算(分塊):

設a'/d=x,那么最后一個a'/d=x的d=a'/x,所以這段連續的區間就是[d,a'/(a'/d)]

結合b'/d,取個min就可以了。

我們就不需要對每一個x都做一個遍歷而是可以分塊把復雜度降到根號n;

bzoj1101:

1101: [POI2007]Zap

Time Limit:10 SecMemory Limit:162 MB
Submit:1400Solved:455
[Submit][Status]

Description

FGD正在破解一段密碼,他需要回答很多類似的問題:對于給定的整數a,b和d,有多少正整數對x,y,滿足x<=a,y<=b,并且gcd(x,y)=d。作為FGD的同學,FGD希望得到你的幫助。

Input

第一行包含一個正整數n,表示一共有n組詢問。(1<=n<= 50000)接下來n行,每行表示一個詢問,每行三個正整數,分別為a,b,d。(1<=d<=a,b<=50000)

Output

對于每組詢問,輸出到輸出文件zap.out一個正整數,表示滿足條件的整數對數。

Sample Input

2
4 5 2
6 4 3

Sample Output

3
2

HINT

對于第一組詢問,滿足條件的整數對有(2,2),(2,4),(4,2)。對于第二組詢問,滿足條件的整數對有(6,3),(3,3)。

參考代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
int prime[100001];
int mu[100001];
int sum[100001];
void init()
{
    int vis[100001];
    memset(vis,0,sizeof(vis));
    memset(mu,0,sizeof(mu));
    memset(prime,0,sizeof(prime));
    mu[1]=1;
    int ret=0;
    for(int i=2;i<=100000;i++)
    {
        if(!vis[i])
        {
            prime[ret++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<ret && i*prime[j] < 100000;j++)
        {
            int temp=i*prime[j];
            vis[temp]=1;
            if(i%prime[j]) mu[temp]=-mu[i];
            else
            {
                mu[temp]=0;
                break;
            }
        }
    }
    for(int i=1;i<=100001;i++)
    {
        sum[i]=sum[i-1]+mu[i];
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    init();
    int Case=0;
    while(t--)
    {
        int b,d,k;
        cin>>b>>d>>k;
        printf("Case %d: ",++Case);
        if(k==0)
        {
            cout<<"0"<<endl;
            continue;
        }
        ll temp1,temp2;
        temp1=temp2=0ll;
        d/=k;
        b/=k;
        int pos;
        for(int i=1;i<=min(b,d);i=pos+1)
        {
            pos=min(b/(b/i),d/(d/i));//求出分塊區間
            temp1+=(sum[pos]-sum[i-1])*(b/i)*(d/i);// 對mu做一個前綴和的預處理
        }
        cout<<temp1<<endl;

    }

    return 0;
}

好以上都是對于求一定范圍內gcd(x,y)=k的情況,那么要求gcd(x,y)為質數的情況呢?

前面求質數的情況已經介紹過了,這里無非就是枚舉一次范圍內質數的個數,但是直接枚舉是會超時的,我們還是要用到分塊的思想。既然要用到分塊的思想那么我們勢必需要對前綴和做一個處理。http://www.cnblogs.com/iwtwiioi/p/4132095.html 這位大佬里面提到了對前綴和的處理,很詳細。

未完待續。。。看了將近三天才處理掉一下皮毛。。加油加油

總結

以上是生活随笔為你收集整理的莫比莫斯小结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。