●HDU 1695 GCD
题链:
http://acm.hdu.edu.cn/showproblem.php?pid=1695
题解:
容斥。
莫比乌斯反演,入门题。
问题化简:求满足x∈(1~n)和y∈(1~m),且gcd(x,y)=1的(x,y)的对数。
下文默认$n \leq m$
1.容斥
(先写了一个的裸的容斥。)
令$f(k)为gcd(x,y)=\lambda k的(x,y)的对数$
$ANS=f(0种质数的积)-f(1种质数的积)+f(2种质数的积)-\cdots+(-1)^mf(m种质数的积)$
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 100500 using namespace std; bool np[MAXN],can[MAXN]; int T[MAXN]; void Prime_Sieve(){ static int prime[MAXN],pnt; for(int i=2;i<=100000;i++){ if(!np[i]) prime[++pnt]=i,T[i]=1,can[i]=1; for(int j=1;j<=pnt&&i<=100000/prime[j];j++){ np[prime[j]*i]=1; T[prime[j]*i]=T[i]+(i%prime[j]!=0); can[prime[j]*i]=can[i]&&(i%prime[j]!=0); if(i%prime[j]==0) break; } } } long long work(int b,int d){ long long ret=1ll*b*d,tmp; for(int i=2;i<=b;i++) if(can[i]){ tmp=(T[i]&1?-1:1)*1ll*(b/i)*(d/i); ret+=tmp; } return ret; } int main(){ Prime_Sieve(); int a,b,c,d,k,Case; long long ans; //while(~scanf("%d",&n)) printf("%d 的质数因子有 %d 种\n",n,T[n]); scanf("%d",&Case); for(int i=1;i<=Case;i++){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if(k==0){printf("Case %d: 0\n",i); continue;} if(b>d) swap(b,d); ans=work(b/k,d/k); ans-=work(b/k,b/k)/2; printf("Case %d: %lld\n",i,ans); } return 0; }
2.莫比乌斯反演
令$f(k)为gcd(x,y)=k的(x,y)的对数$
$F(k)为gcd(x,y)=\lambda k的(x,y)的对数$
显然$F(k)=\sum_{k|d}{f(d)},且F(k)=\lfloor\frac{n}{k}\rfloor\times\lfloor\frac{m}{k}\rfloor$
那么由莫比乌斯反演公式的形式二(倍数关系那个式子):
即 $\mathbf{f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d),需满足F(n)=\sum_{n|d}f(d)}$
所以我们要求的答案:$f(1)$可以化为如下形式:
$f(1)=\sum_{1|d}\mu(\frac{d}{1})F(d)$
$\quad\quad=\sum_{d=1}^{n}\mu(d)\times\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor$
感觉还是$O(n)$的呀,和上面的容斥没什么区别呢?
其实对于这种式子,有一个trick可以把其复杂度优化到$O(\sqrt{n})$
看看这样一个例子:
$\lfloor \frac{100}{34} \rfloor=\lfloor \frac{100}{35} \rfloor=\cdots=\lfloor\frac{100}{50}\rfloor=2$
即在向下取整的情况下,$F(k)$函数会有很多段相同的取值,
所以可以靠这个来把时间优化到$O(\sqrt{n})$。
具体实现看下面代码中的$work()函数$,(学习别人的,很巧妙,很简洁,但是也很迷。。。)
/* http://acm.hdu.edu.cn/showproblem.php?pid=1695 莫比乌斯反演,入门题。 令 f(k)=gcd(x,y) */ #include<cstdio> #include<cstring> #include<iostream> #define MAXN 100500 using namespace std; int mu[MAXN],pmu[MAXN]; void Mobius_Sieve(){ static bool np[MAXN]; mu[1]=1; pmu[1]=1; static int prime[MAXN],pnt; for(int i=2;i<=100000;i++){ if(!np[i]) prime[++pnt]=i,mu[i]=-1; pmu[i]=pmu[i-1]+mu[i]; for(int j=1;j<=pnt&&i<=100000/prime[j];j++){ np[prime[j]*i]=1; if(i%prime[j]) mu[i*prime[j]]=-mu[i]; else mu[i*prime[j]]=0; if(i%prime[j]==0) break; } } } long long work(int b,int d){ long long ret=0,tmp; for(int i=1,last;i<=b;i=last+1){ last=min(b/(b/i),d/(d/i)); tmp=1ll*(pmu[last]-pmu[i-1])*(b/i)*(d/i); ret+=tmp; } return ret; } int main(){ Mobius_Sieve(); int a,b,c,d,k,Case; long long ans; scanf("%d",&Case); for(int i=1;i<=Case;i++){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if(k==0){printf("Case %d: 0\n",i); continue;} if(b>d) swap(b,d); ans=work(b/k,d/k); ans-=work(b/k,b/k)/2; printf("Case %d: %lld\n",i,ans); } return 0; }
(诶,这个莫比乌斯反演得到的式子和那个容斥的到的好像是一样的!)
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas