题解 P5348 【密码解锁】
数论毒瘤题
后面根本不会做了,什么求和套路都用光了。
所以还是自己容斥学的太差了。。。
不过这题简直就是多合一啊
讲讲我的心路历程:
做这道题之前请做一做
顺便安利一下我的题解
前面都是自己瞎推的,后面思路参考了楼上的大佬。
题意:
设一个函数
求
然后很显然的用莫比乌斯反演定理的第
复习一下前几天还说莫比乌斯反演定理没用
若
则
证明:
设
证完了。
所以我们用一用莫比乌斯反演定理就有
套路去设
这就又用到了循环之美的套路了,不会的可以看一看我的小结中的第
所以就可以拆掉了
这前面都是自己推的,然后后面我就懵逼了
怎么求这个式子
我想到了
考虑没有
我们要求的就是
然后根据枚举平方因子的倍数个数,然后容斥,且容斥系数为
不懂的可以看我的题解或者
其实现在就多了一个条件,这个数和
容斥容斥,搞不出来,样例没过。。。
然后就参考了
我们容斥的不是
它的意义是什么?不就是平方数的倍数个数吗?
只要这个平方数的倍数和
设
必须要有
即
设
所以我们现在要求的不就是
设
然后怎么求
我的思路和巨佬居然是类似的!惊喜!!!
我们分类讨论
如果
所以
否则,
这个继续用套路
枚举
然后这道题就做完了,设
对了不要忘记外层还有一个
看不懂的还可以私信问我。
具体实现非常恶心?
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,T,p[100001],d[100001],arfa,tot,vis[100001],mu[100001],MU[100001];
inline void init(ll nx){
mu[1]=1;
for (register int i=2;i<=nx;i++){
if (!vis[i]) p[++tot]=i,mu[i]=-1;
for (register int j=1;j<=tot&&(ll)i*p[j]<=nx;j++){
if (1LL*i*p[j]>nx) continue;
vis[1LL*i*p[j]]=1;
if((i%p[j])==0){
mu[1LL*i*p[j]]=0;
break;
}
mu[1LL*i*p[j]]=-mu[i];
}
}
}
ll Mu(ll a){//求单个数的莫比乌斯函数
if (a<=35001) return mu[a];
ll x=a,cnt=0;
for (ll j=2;j*j<=a;j++){
if (x%j==0){
ll now=0;
while (x%j==0) now++,x/=j;
if (now>1) return 0;
cnt++;
}
}
if (x!=1) cnt++;
return (cnt&1)?-1:1;
}
inline ll h(ll I){
if (__gcd(I,m)!=1) return 0;
ll ans=0;
for (int i=1;i<=arfa;i++){
ans+=MU[i]*(n/m/(I*I)/d[i]);
}
return ans;
}
int main(){
init(35005);
cin>>T;
while (T--){
cin>>n>>m;
arfa=0;
for (int i=1;i*i<=m;i++){
if (m%i==0) d[++arfa]=m/i,d[++arfa]=i;//枚举因数
}
if (d[arfa]==d[arfa-1]) d[arfa--]=0;
for (int i=1;i<=arfa;i++){
MU[i]=Mu(d[i]);//预处理每一个因数的莫比乌斯函数
}
ll ans=0;
for (register int i=1;i*i<=n/m;i++){
ans+=mu[i]*h(i);
}
printf("%lld\n",Mu(m)*ans);
}
}