莫比莫斯小结
前幾天多校的時候碰到一道莫比烏斯的題目,一臉懵逼,然后惡補了幾道。
先看下莫比烏斯的基本概念:
定理:和是定義在非負整數集合上的兩個函數,并且滿足條件,那么我們得到結論
在上面的公式中有一個函數,它的定義如下:
(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 這位大佬里面提到了對前綴和的處理,很詳細。
未完待續。。。看了將近三天才處理掉一下皮毛。。加油加油
總結
- 上一篇: 魏牌举办试驾活动 厂家竟要求先试问界M5
- 下一篇: 英特尔CPU与手机大小核设计有何不同?来