[NOI2016]循环之美
[NOI2016]循环之美
对于已知的十进制数\(n\)和\(m\),在\(k\)进制下,有多少个数值上互不相等的纯循环小数,可以用分数\(\frac xy\)表示,其中\(1\leq x\leq n,1\leq y\leq m\),且\(x,y\in\mathbb{Z}\)
首先\(k\)进制下如果\((y,k)=1\),那么\(\frac xy\)是纯循环的。当然还要满足\((x,y)=1\)。证明略
那么答案是:\(f(n,m,k)=\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1][(j,k)=1]\)
我也不知道是怎么想到推过去的。。。可能考场上只能dfs推公式了。。
\(\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]\sum_{d|j,d|k}\mu(d)\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{d|j}^m[(i,j)=1]\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\frac md}[(i,jd)=1]\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\frac md}[(i,j)=1][(i,d)=1]\)
\(\sum_{d|k}\mu(d)f(\frac md,n,k)\)
可以暴力枚举约数,用map记忆化
一个很有用的优化,如果\(\mu(d)=0\)则不用递归下去
边界条件就是\(f(n,m,1)\)
\(f(n,m,1)=\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]=\sum_{d=1}^n\mu(d)\lfloor\frac nd\rfloor\lfloor\frac md\rfloor(n\leq m)\)
杜教筛即可
#include<bits/stdc++.h>
#define il inline
#define vd void
#define ull unsigned long long
typedef long long ll;
il ll gi(){
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch))f^=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f?x:-x;
}
#define maxn 2000000
int pr[maxn],p,mu[maxn];
bool yes[maxn];
std::unordered_map<int,int>_smu;
il int smu(int n){
if(n<maxn)return mu[n];
if(_smu[n])return _smu[n];
int ret=1,qt=sqrt(n);
for(int i=2;i<=qt;++i)ret-=smu(n/i);
for(int i=qt+1,j;i<=n;i=j+1)j=n/(n/i),ret-=smu(n/i)*(j-i+1);
return _smu[n]=ret;
}
std::vector<int>d[2010];
int muqwq[2010];
struct data{int n,m,k;};
std::map<data,ll>_solve;
il bool operator<(const data&a,const data&b){
if(a.n!=b.n)return a.n<b.n;
if(a.m!=b.m)return a.m<b.m;
return a.k<b.k;
}
il ll solve(int n,int m,int k){
if(!n||!m)return 0;
data qwq={n,m,k};
if(_solve.find(qwq)!=_solve.end())return _solve[qwq];
ll ret=0;
if(k==1){
if(n>m)std::swap(n,m);
int lst=0,smuj;
for(int i=1,j;i<=n;i=j+1){
j=std::min(n/(n/i),m/(m/i));
smuj=smu(j);
ret+=1ll*(smuj-lst)*(n/i)*(m/i);
lst=smuj;
}
return ret;
}else for(auto i:d[k])ret+=solve(m/i,n,i)*muqwq[i];
return _solve[qwq]=ret;
}
ll F[2010];
int main(){
int n=gi(),m=gi(),k=gi();
mu[1]=1;
for(int i=2;i<maxn;++i){
if(!yes[i])pr[++p]=i,mu[i]=-1;
for(int j=1;j<=p&&i*pr[j]<maxn;++j){
yes[i*pr[j]]=1;
if(i%pr[j])mu[i*pr[j]]=-mu[i];
else{mu[i*pr[j]]=0;break;}
}
}
for(int i=1;i<=k;++i)muqwq[i]=mu[i];
for(int i=1;i<maxn;++i)mu[i]+=mu[i-1];
for(int i=1;i<=k;++i)if(k%i==0&&muqwq[i])for(int j=i;j<=k;j+=i)d[j].emplace_back(i);
printf("%lld\n",solve(n,m,k));
#ifdef DEBUG
fprintf(stderr,"%.6lf\n",1.0*clock()/CLOCKS_PER_SEC);
#endif
return 0;
}
本博客中博文均为原创,未经博主允许请勿随意转载,谢谢。