[NOI2016]循环之美

题如其名,这道题十分优美

首先,如果$\dfrac xy$在$k$进制下是循环节长度为$l$的纯循环小数,那么$\dfrac xy$和$\dfrac{xk^l}y$的小数部分是一样的,也就是说$x\equiv xk^l(\bmod y)$,即$k^l\equiv1(\bmod y)$,这时就有$(k,y)=1$,显然以上每一步都是充分必要的

所以我们要求$\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(i,j)=1][(j,k)=1]$,先推一下式子

$\begin{aligned}\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(i,j)=1][(j,k)=1]&=\sum\limits_{j=1}^m[(j,k)=1]\sum\limits_{i=1}^n\sum\limits_{\substack{d|i\\d|j}}\mu(d)\\&=\sum\limits_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac nd\right\rfloor\sum\limits_{j=1}^{\left\lfloor\frac md\right\rfloor}[(jd,k)=1]\\&=\sum\limits_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac nd\right\rfloor[(d,k)=1]\sum\limits_{j=1}^{\left\lfloor\frac md\right\rfloor}[(j,k)=1]\end{aligned}$

现在我们要求两个东西:$f(n)=\sum\limits_{i=1}^n[(i,k)=1]$和$g(n,k)=\sum\limits_{i=1}^n\mu(i)[(i,k)=1]$

$f(n)=\left\lfloor\frac nk\right\rfloor f(k)+f(n\%k)$,预处理$f(1\cdots k)$即可

$\begin{aligned}g(n,k)&=\sum\limits_{i=1}^n\mu(i)[(i,k)=1]\\&=\sum\limits_{i=1}^n\mu(i)\sum\limits_{\substack{d|i\\d|k}}\mu(d)\\&=\sum\limits_{d|k}\mu(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(id)\\&=\sum\limits_{d|k}\mu(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(id)[(i,d)=1]\\&=\sum\limits_{d|k}\mu^2(d)\sum\limits_{i=1}^{\left\lfloor\frac nd\right\rfloor}\mu(i)[(i,d)=1]\\&=\sum\limits_{d|k}\mu^2(d)g\left(\left\lfloor\frac nd\right\rfloor,d\right)\end{aligned}$

中间多出来的中括号不是无中生有,因为如果$(i,d)\ne1$那么$\mu(id)=0$,这一步是比较妙的地方

于是我们可以递归求值,递归到$k=1$时直接杜教筛,这里的时间复杂度是$O(n^{\frac23})$,观察递归式,我们可以只分析$d$的取值,有$O(\sigma_0(k))$个不同的$d$,每次转移耗费时间为$O(\sigma_0(d))$,加上最外层的根号分段,总时间复杂度为$O\left(n^{\frac23}+\sigma_0^2(k)\sqrt n\right)$,当然这个上界很松,所以跑得还是挺快的

The beautiful math.

#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int T=1000000;
int pr[T+10],mu[T+10],smu[T+10];
bool np[T+10];
void sieve(){
	int i,j,M=0;
	mu[1]=1;
	for(i=2;i<=T;i++){
		if(!np[i]){
			pr[++M]=i;
			mu[i]=-1;
		}
		for(j=1;j<=M&&i*pr[j]<=T;j++){
			np[i*pr[j]]=1;
			if(i%pr[j]==0)break;
			mu[i*pr[j]]=-mu[i];
		}
	}
	for(i=1;i<=T;i++)smu[i]=smu[i-1]+mu[i];
}
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int f[2010];
map<int,int>g[2010];
int get(int n,int k){
	if(n==0)return 0;
	if(g[k].count(n))return g[k][n];
	if(k==1){
		if(n<=T)return smu[n];
		if(g[1].count(n))return g[1][n];
		int i,nex,s;
		s=1;
		for(i=2;i<=n;i=nex+1){
			nex=n/(n/i);
			s-=(nex-i+1)*get(n/i,1);
		}
		return g[1][n]=s;
	}
	int i,s;
	s=0;
	for(i=1;i*i<=k;i++){
		if(k%i==0){
			if(mu[i])s+=get(n/i,i);
			if(i*i!=k&&mu[k/i])s+=get(n/(k/i),k/i);
		}
	}
	return g[k][n]=s;
}
int main(){
	sieve();
	int n,m,k,i,N,nex;
	ll s;
	scanf("%d%d%d",&n,&m,&k);
	for(i=1;i<=k;i++)f[i]=f[i-1]+(gcd(i,k)==1);
	N=min(n,m);
	s=0;
	for(i=1;i<=N;i=nex+1){
		nex=min(n/(n/i),m/(m/i));
		s+=(n/i)*((m/i/k)*(ll)f[k]+f[m/i%k])*(get(nex,k)-get(i-1,k));
	}
	printf("%lld",s);
}

转载于:https://www.cnblogs.com/jefflyy/p/9503906.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值