莫比莫斯小结
前幾天多校的時(shí)候碰到一道莫比烏斯的題目,一臉懵逼,然后惡補(bǔ)了幾道。
先看下莫比烏斯的基本概念:
定理:和是定義在非負(fù)整數(shù)集合上的兩個(gè)函數(shù),并且滿(mǎn)足條件,那么我們得到結(jié)論
在上面的公式中有一個(gè)函數(shù),它的定義如下:
(1)若,那么
(2)若,均為互異素?cái)?shù),那么
(3)其它情況下
來(lái)自acdreamer大佬的博客。
先說(shuō)下第一道入門(mén)題目(雖然入門(mén),處理的東西也不少。。):
hdu 1695:http://acm.hdu.edu.cn/showproblem.php?pid=1695
題目意思:對(duì)于1<=x<=d,1<=y<=d 求gcd(x,y)=k的個(gè)數(shù)
題解:首先第一步,對(duì)于對(duì)于1<=x<=d,1<=y<=d 如果 gcd(x,y)=k,那么對(duì)于1<=x/k<=d/k,1<=y/k<=d/k gcd(x,y)=1。問(wèn)題就轉(zhuǎn)化為對(duì)于1<=x<=d/k,1<=y<=d/k 求gcd(x,y)=1的個(gè)數(shù)。
然后我們定義兩個(gè)函數(shù)。F(n)與f(n);
F(d)為 有多少對(duì)(x,y)滿(mǎn)足gcd(x,y)== d 的倍數(shù) 。
f(d)為有多少對(duì)(x,y)滿(mǎn)足gcd(x,y)== d 。
我們要求的是f(d)的值,但我們發(fā)現(xiàn)F(d)函數(shù)比較容易求解F(d)=(n/d)*(m/d);(這個(gè)怎么理解呢,我們把范圍1~n里面能夠整除x的數(shù)的個(gè)數(shù)表示為n/x;對(duì)于兩個(gè)數(shù),如果兩個(gè)數(shù)的因子都有d,那么這兩個(gè)數(shù)的倍數(shù)的gcd一定是d的倍數(shù))。
是不是可以套進(jìn)去?
而且我們簡(jiǎn)化之后,要求的n為1 那么u也就很好求了。
貼一個(gè)線(xiàn)性求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;
}
}
}
有了這些之后,還有一個(gè)問(wèn)題。題目要求去重,這個(gè)怎么理解呢?
此時(shí),走到這一步,我們已經(jīng)求得了(x,y)滿(mǎn)足gcd(x,y)=1的對(duì)數(shù) ,但題目中說(shuō)明了,(1,2)和(2,1)算一種情況,那么我們就要減去多余了的情況,怎那么找出那些多算進(jìn)去的情況呢? 下面的圖畫(huà)的很清楚:
G(b,b)就是多算進(jìn)去的這些情況,
那么 G(b,d)- G(b,b)/ 2 就是最終我們要求的結(jié)果了,至于這一點(diǎn),有不懂的請(qǐng)?jiān)诩埳袭?huà)一畫(huà),這不是我要講的重點(diǎn)了。(轉(zhuǎn)自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;
}
對(duì)于這個(gè)問(wèn)題還有一個(gè)知識(shí)點(diǎn)可以,就是分塊求和。
對(duì)于[n/x]這個(gè)表達(dá)式,當(dāng)[n/x]=d是不是有很多x可以對(duì)應(yīng)這個(gè)值?
比如a'=100,那么d在[34,50]之間a'/d都是2。
那么我們可以把連續(xù)的一段d一起來(lái)算(分塊):
設(shè)a'/d=x,那么最后一個(gè)a'/d=x的d=a'/x,所以這段連續(xù)的區(qū)間就是[d,a'/(a'/d)]
結(jié)合b'/d,取個(gè)min就可以了。
我們就不需要對(duì)每一個(gè)x都做一個(gè)遍歷而是可以分塊把復(fù)雜度降到根號(hào)n;
bzoj1101:
1101: [POI2007]Zap
Time Limit:10 SecMemory Limit:162 MB
Submit:1400Solved:455
[Submit][Status]
Description
FGD正在破解一段密碼,他需要回答很多類(lèi)似的問(wèn)題:對(duì)于給定的整數(shù)a,b和d,有多少正整數(shù)對(duì)x,y,滿(mǎn)足x<=a,y<=b,并且gcd(x,y)=d。作為FGD的同學(xué),F(xiàn)GD希望得到你的幫助。
Input
第一行包含一個(gè)正整數(shù)n,表示一共有n組詢(xún)問(wèn)。(1<=n<= 50000)接下來(lái)n行,每行表示一個(gè)詢(xún)問(wèn),每行三個(gè)正整數(shù),分別為a,b,d。(1<=d<=a,b<=50000)
Output
對(duì)于每組詢(xún)問(wèn),輸出到輸出文件zap.out一個(gè)正整數(shù),表示滿(mǎn)足條件的整數(shù)對(duì)數(shù)。
Sample Input
2
4 5 2
6 4 3
Sample Output
3
2
HINT
對(duì)于第一組詢(xún)問(wèn),滿(mǎn)足條件的整數(shù)對(duì)有(2,2),(2,4),(4,2)。對(duì)于第二組詢(xún)問(wèn),滿(mǎn)足條件的整數(shù)對(duì)有(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));//求出分塊區(qū)間
temp1+=(sum[pos]-sum[i-1])*(b/i)*(d/i);// 對(duì)mu做一個(gè)前綴和的預(yù)處理
}
cout<<temp1<<endl;
}
return 0;
}
好以上都是對(duì)于求一定范圍內(nèi)gcd(x,y)=k的情況,那么要求gcd(x,y)為質(zhì)數(shù)的情況呢?
前面求質(zhì)數(shù)的情況已經(jīng)介紹過(guò)了,這里無(wú)非就是枚舉一次范圍內(nèi)質(zhì)數(shù)的個(gè)數(shù),但是直接枚舉是會(huì)超時(shí)的,我們還是要用到分塊的思想。既然要用到分塊的思想那么我們勢(shì)必需要對(duì)前綴和做一個(gè)處理。http://www.cnblogs.com/iwtwiioi/p/4132095.html 這位大佬里面提到了對(duì)前綴和的處理,很詳細(xì)。
未完待續(xù)。。??戳藢⒔觳盘幚淼粢幌缕っ?。。加油加油
總結(jié)
- 上一篇: 魏牌举办试驾活动 厂家竟要求先试问界M5
- 下一篇: 英特尔CPU与手机大小核设计有何不同?来