uoj221【NOI2016】循环之美

前面部分比较简单,就是无脑化式子,简单点讲好了。
首先肯定在\((x,y)=1\)时才考虑这个分数,要求纯循环的话,不妨猜猜结论,就是y必须和K互质。所以答案是\(\sum_{i=1}^n \sum_{j=1}^m [(i,j)=1] [(j,k)=1]\)

然后用 \([(i,j)=1]=\sum_{d|i,j} \mu(d)\)大力化一化,很快就会得到:

\[\sum_{d=1}^{min(n,m)} \mu(d) \frac{n}{d} \sum_{d|j,j\le m}[(j,k)=1] \]

\[=\sum_{d=1}^{min(n,m)} \mu(d) [(d,k)=1] \frac{n}{d} \sum_{j=1}^{\frac{m}{d}}[(j,k)=1] \]

令后面那一坨\(\sum_{j=1}^{\frac{m}{d}}[(j,k)=1]=f(\frac{m}{d})\),它可以快速计算:

\[f(x)=\sum_{j=1}^x [(j,K)=1] \]

\[=\sum_{j=1}^x \sum_{g|j,k} \mu(g) \]

\[=\sum_{g|k}\mu(g) \sum_{g|j} 1 \]

\[=\sum_{g|k}\mu(g) \frac{x}{g} \]

可以\(O(\sqrt k)\)计算。

回到原式

\[\sum_{d=1}^{min(n,m)} \mu(d) [(d,k)=1] \frac{n}{d} f(\frac{m}{d}) \]

这个显然可以分块吧,预处理一下\(\sum_{d=1}^{min(n,m)} \mu(d)[(d,k)=1]\)的前缀和就可以\(O(\sqrt n *\sqrt k)\)算答案了,因为是gcd的log,预处理做到2e7都不虚。
然后就有84分了。

考虑快速求\(F(k,x)=\sum_{d=1}^x \mu(d)*[(d,k)==1]\),同样拆后面的gcd。

\[F(k,x) =\sum_{d=1}^x \mu(d)*[(d,k)==1] \]

\[=\sum_{d=1}^x \mu(d) \sum_{g|k,d} \mu(g) \]

\[=\sum_{g|k} \mu(g) \sum_{g|d} \mu(d) \]

\[=\sum_{g|k} \mu(g) \sum_{T=1}^{\frac{x}{g}} \mu(T*g) \]

然后由于当\((T,g)\ne 1\)\(\mu(T*g)\)显然=0。

\[=\sum_{g|k} \mu(g) \sum_{T=1}^{\frac{x}{g}} [(T,g)==1]*\mu(T)*\mu(g) \]

\[=\sum_{g|k} \mu^2(g) \sum_{T=1}^{\frac{x}{g}} \mu(T)*[(T,g)==1] \]

\[=\sum_{g|k} \mu^2(g) F(g,\frac{x}{g}) \]

然后递归算,顺便记忆化一下,另外当k=1时直接返回\(\sum_{i=1}^x \mu(i)\),因此要杜教筛预处理。

\(F(k,i)\)可以预处理一下\(k=K\)时x较小的若干项,会加快速度。

我根本不会算这个的复杂度,想到这后就直接去写了,极限数据一下就跑出来了就交了,别问我复杂度是多少,我不知道。复杂度应该和杜教筛差不多吧(如果对g讨论一下在x的\(\sqrt x\)段中的哪一段,这一段的k的约数统一计算的话)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#define pl puts("lala")
#define cp cerr<<"lala"<<endl
#define fi first
#define se second
#define pb push_back
#define ln putchar('\n')
using namespace std;
inline int read()
{
	char ch=getchar();int g=1,re=0;
	while(ch<'0'||ch>'9') {if(ch=='-')g=-1;ch=getchar();}
	while(ch<='9'&&ch>='0') re=(re<<1)+(re<<3)+(ch^48),ch=getchar();
	return re*g;
}
typedef long long ll;
typedef pair<int,int> pii;

inline int gcd(int a,int b)
{
	while(b) {int t=a%b; a=b; b=t;}
	return a;
}
const int N=7e5+11;
int prime[N],cnt=0,mu[N],smu[N],resf[N],K;
bool isnotprime[N];
void init()
{
	int n=N-11;
	isnotprime[1]=1; mu[1]=1;
	for(int i=2;i<=n;++i) 
	{
		if(!isnotprime[i]) prime[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*prime[j]<=n;++j)
		{
			isnotprime[i*prime[j]]=1;
			if(!(i%prime[j])) break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;++i) smu[i]=smu[i-1]+mu[i];
	for(int i=1;i<=n;++i) resf[i]=resf[i-1]+mu[i]*(gcd(i,K)==1);
}

map<int,int>mp;
int M(int n)
{
	if(n<=N-11) return smu[n];
	if(mp.count(n)) return mp[n];
	int &ans=mp[n]; ans=1;
	for(int i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		ans-=M(n/i)*(j-i+1);
	}
	return ans;
}

map<int,int>F[2050];
int calcF(int k,int x)
{
	if(k==1) return M(x);
	if(k==K&&x<=N-11) return resf[x];
	if(F[k].count(x)) return F[k][x];
	int &ans=F[k][x];
	for(int i=1;i*i<=k;++i) if(!(k%i))
	{
		if(mu[i]) ans+=mu[i]*mu[i]*calcF(i,x/i);
		if(i*i!=k) 
		{
			int oth=k/i;
			if(mu[oth]) ans+=mu[oth]*mu[oth]*calcF(oth,x/oth);
		}
	}
	return ans;
}

int n,m;
int divi[2050],tot=0;
inline int f(int x)
{
	int ans=0;
	for(int i=1;i<=tot;++i) ans+=mu[divi[i]]*(x/divi[i]);
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in","r",stdin);freopen("1.out","w",stdout);
#endif
	n=read(); m=read(); K=read();
	init();
	int mn=min(n,m);
	M(mn);
	for(int i=1;i*i<=K;++i) if(!(K%i))
	{
		divi[++tot]=i;
		if(i*i!=K) divi[++tot]=K/i;
	}
	
	ll ans=0;
	for(int i=1,j;i<=mn;i=j+1)
	{
		j=min(n/(n/i),m/(m/i));
		ans+=1ll*(n/i)*f(m/i)*(calcF(K,j)-calcF(K,i-1));
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2018-05-02 20:38  BLMontgomery  阅读(204)  评论(0编辑  收藏  举报