近几天做了几道有关反演的问题,在此集合一下吧。
1、[BZOJ 2301]HAOI2011 Problem b
2、[BZOJ 2440]中山市选2011 完全平方数
3、gcd
4、[BZOJ 2186]SDOI2008 莎拉公主的困惑
5、[BZOJ 3529]SDOI2014 数表
(蒟蒻自认为反演一类的的题目重要的就是记住两个重要的公式:1、sigma(mu[i] , i|n ) = [n==1] 2、sigma(phi[i] , i|n ) = n)
题解:
1、[BZOJ 2301]HAOI2011 Problem b
这基本上算是入门题了吧。。。。
gcd(a,b)==k等价于gcd(a/k,b/k)==1,这样我们将范围除k,等价到求区间内互质的二元组数。
假设query(n,m)得到gcd(a,b)==1的数量,那么答案很明显就是gcd(b,d) - gcd(a-1,d) - gcd(b,c-1) + gcd(a-1,c-1)
简单了,下面简述一下推导过程吧
为了不被多组数据卡掉,我们观察到n/k的答案只有不超过根号n个不同答案,对mu[]前缀和优化,那么每次询问时O(sqrt(n))的复杂度
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int l,r,ans,mid,INF,K,Case,tot;
int prime[41005],mu[41005],i,j;
bool check[41005];
void init(){
mu[1]=1;
for (i=2;i<41000;i++){
if (!check[i]) mu[i]=-1, prime[++tot]=i;
for (j=1;j<=tot;j++){
if (i*prime[j]>41000) break;
check[i*prime[j]]=1;
if (i%prime[j]==0) {mu[i*prime[j]]=0;break;}
else mu[i*prime[j]] = -mu[i];
}
}
}
bool Judge(){
int ret=0;
for (int i=1;i*i<=mid;i++)
ret+=(mid/i/i)*mu[i];
return (ret>=K);
}
int main(){
//freopen("2440.in","r",stdin);
//freopen("2440.out","w",stdout);
init();
INF=41000; INF=INF*INF;
scanf("%d",&Case);
while (Case--){
scanf("%d",&K);
l=1; r=INF;
while (l<=r){
mid=((long long)l+r)>>1;
if (Judge()) ans=mid, r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}
2、[BZOJ 2440]中山市选2011