hdu 5072 Coprime(容斥+快速統計cnt個數與x互質的個數)
阿新 • • 發佈:2018-12-19
題目連結:
轉換成求滿足條件的反面: ①:a和b,c互質,b和c不互質 ②:a和b,c不互質,b和c互質 於是就是列舉每個數,找出與他互質的個數與不互質的個數 答案就是:互質個數*不互質個數/2 至於為什麼要除2,我看得還不是很懂
然後就變成了:怎樣快速統計cnt個數與x互質的個數? 以前看過m以內與x互質的個數,也是容斥來求,但是那是m以內的每個數,而現在是隨便cnt個數,這cnt個數長什麼樣子都不知道,怎麼辦呢?
我們看這個數與其他數互不互質就是看這個數與其他數有沒有公共的質因子,如果有,那就不互質,於是就篩出這n個數中含有某個因子 i 的個數,用cnt2[i] 來表示 那麼看某個數著n個數中的某個數 x 與他不互質
其實感覺跟質因子有關的容斥,就跟莫比烏斯函式有關,上面的容斥係數就是莫比烏斯函式,所以闊以用莫比烏斯函式來直接求與這個數 x 互質的個數
①容斥做
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e9+7;
int cnt1[maxn];//記錄i這個數有多少個
int cnt2[maxn];//看含有因子i 的數有多少個
vector<LL> factor[maxn];//用來儲存這個數的質因子
vector<LL>prime;
int mu[maxn];
LL Max;
LL N;
bool vis[maxn];
void PHI(int n)
{
memset(vis,1,sizeof(vis));
mu[1]=1;
for(int i=2; i<=n; i++)
{
if(vis[i])
{
prime.push_back(i);
mu[i]=-1;
}
for(int j=0; j<prime.size()&&i*prime[j]<=n; j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
}
void fenjie(LL n)//分解質因子
{
if(factor[n].size())return ;
LL t=0,m=n;
while(n>1)
{
if(n%prime[t]==0)
{
factor[m].push_back(prime[t]);
while(n%prime[t]==0)n/=prime[t];
}
if(prime[t]*prime[t]>n)break;
t++;
}
if(n>1)factor[m].push_back(n);
}
LL calc(LL n)//計算這個數與其他數互質的個數
{
LL res=0;
LL cnt=factor[n].size();
for(int sta=1; sta<(1<<cnt); sta++)
{
LL tp=1;
for(int j=0; j<cnt; j++)
{
if(sta&(1<<j))tp*=factor[n][j];
}
if(__builtin_popcount(sta)%2)res+=cnt2[tp]-1;//減1是減去他自己
else res-=cnt2[tp]-1;
}
return res;
}
int a[maxn];
int main()
{
PHI(maxn-5);
int T;
cin>>T;
while(T--)
{
LL Max=0;
for(int i=0;i<=100000;i++)cnt1[i]=cnt2[i]=0;
cin>>N;
for(int i=1; i<=N; i++)
{
int t;
scanf("%d",a+i);
Max=max(Max,(LL)a[i]);
cnt1[a[i]]++;
fenjie(a[i]);
}
for(int t=1; t<=Max; t++)for(int k=1; k*t<=Max; k++)cnt2[t]+=cnt1[k*t]; //列舉t的倍數
LL res=0;
for(int i=1;i<=N;i++)
{
LL tp;
tp=calc(a[i]);//求的是與他不互質的個數
res+=(N-1-tp)*tp;
}
cout<<N*(N-1)*(N-2)/6-res/2<<endl;
}
}
②莫比烏斯函式來求
直接求會T,但是好理解
求答案的時間大概要400+ms,然後前面還有100+ms,一共5組資料,就會T
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e9+7;
int cnt1[maxn];//記錄i這個數有多少個
int cnt2[maxn];//看含有因子i 的數有多少個
vector<LL>factor[maxn];//用來儲存這個數的質因子
vector<LL>prime;
int mu[maxn];
LL Max;
LL N;
bool vis[maxn];
void PHI(int n)
{
memset(vis,1,sizeof(vis));
mu[1]=1;
for(int i=2; i<=n; i++)
{
if(vis[i])
{
prime.push_back(i);
mu[i]=-1;
}
for(int j=0; j<prime.size()&&i*prime[j]<=n; j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
}
LL calc(LL n)//直接用莫比烏斯函式來容斥 ,求的是與n互質的個數
{
if(n==1)return 0;
LL res=0;
for(LL i=1; i*i<=n; i++)
{
if(n%i)continue;
res+=mu[i]*cnt2[i];
if(i==n/i)continue;
res+=mu[n/i]*cnt2[n/i];
}
return res;
}
int a[maxn];
int main()
{
PHI(maxn-5);
int T;
cin>>T;
while(T--)
{
LL Max=0;
for(int i=0;i<=100000;i++)cnt1[i]=cnt2[i]=0;
cin>>N;
for(int i=1; i<=N; i++)
{
int t;
scanf("%d",a+i);
Max=max(Max,(LL)a[i]);
cnt1[a[i]]++;
}
for(int t=1; t<=Max; t++)for(int k=1; k*t<=Max; k++)cnt2[t]+=cnt1[k*t]; //列舉t的倍數
LL res=0;
for(int i=1;i<=N;i++)
{
LL tp;
tp=calc(a[i]);//求的是與他互質的個數,會超時
res+=(N-1-tp)*tp;
}
cout<<N*(N-1)*(N-2)/6-res/2<<endl;
}
}
計算每個因子的貢獻
就是說上面每個數求的時候,闊以把每個因子的貢獻一口氣加到他所有的倍數上,這樣就能節約很多時間。這種方法就是很常見的優化方法,也是我學這種的時候智商不夠用的方法
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e9+7;
int cnt1[maxn];//記錄i這個數有多少個
int cnt2[maxn];//看含有因子i 的數有多少個
int cnt3[maxn];//用來儲存n個數中與i這個數互質的有幾個
vector<LL>factor[maxn];//用來儲存這個數的質因子
vector<LL>prime;
int mu[maxn];
LL Max;
LL N;
bool vis[maxn];
void PHI(int n)
{
memset(vis,1,sizeof(vis));
mu[1]=1;
for(int i=2; i<=n; i++)
{
if(vis[i])
{
prime.push_back(i);
mu[i]=-1;
}
for(int j=0; j<prime.size()&&i*prime[j]<=n; j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
}
int a[maxn];
int main()
{
PHI(maxn-5);
int T;
cin>>T;
while(T--)
{
int Max=0;
for(int i=0; i<=100000; i++)cnt1[i]=cnt2[i]=cnt3[i]=0;
cin>>N;
for(int i=1; i<=N; i++)
{
scanf("%d",a+i);
Max=max(Max,a[i]);
cnt1[a[i]]++;
}
for(int t=1; t<=Max; t++)
{
//列舉t的倍數,計算t這個數對倍數的貢獻
for(int k=1; k*t<=Max; k++)cnt2[t]+=cnt1[k*t];//計算是t的倍數的數有幾個
for(int k=1; k*t<=Max; k++)cnt3[t*k]+=mu[t]*cnt2[t]; //計算t的貢獻
}
LL res=0;
for(int i=1; i<=N; i++)
{
if(a[i]==1||cnt3[a[i]]==0)continue;
res+=cnt3[a[i]]*(N-1-cnt3[a[i]]);
}
cout<<N*(N-1)*(N-2)/6-res/2<<endl;
}
}