4.10 省选模拟赛 约数 数论 转换 三元组个数

avatar
avatar

题目很简明 可是 上午反演的时候无计可施。

直接做是nln的 可以简单转换一下 原式=(sum_{x=1}^nfrac{n}{x}d(x))

这样就有一个O(n)的做法了 由于d是积性函数直接筛就好了。没必要整除分块 直接O(n).

const int MAXN=15000010;
int n,top;
int v[MAXN],g[MAXN],p[1000010],d[MAXN];
inline void prepare()
{
	d[1]=1;
	rep(2,n,i)
	{
		if(!v[i])
		{
			p[++top]=g[i]=v[i]=i;
			ll sum=p[top];int j=1;
			while(sum<=n)d[sum]=j+1,sum=sum*p[top],++j;
		}
		rep(1,top,j)
		{
			if(n/i<p[j])break;
			int ww=i*p[j];
			v[ww]=p[j];
			if(p[j]==v[i])
			{
				g[ww]=g[i]*p[j];
				d[ww]=d[i/g[i]]*d[g[ww]];
				break;
			}
			d[ww]=d[i]*d[p[j]];
			g[ww]=p[j];
		}
	}
}
int main()
{
	//freopen("divisor.in","r",stdin);
	//freopen("divisor.out","w",stdout);
	get(n);
	if(n<=15000000)
	{
		prepare();//put(top);
		//rep(1,n,i)put(d[i]);
		ll sum=0;
		rep(1,n,i)sum=sum+(ll)(n/i)*d[i];
		putl(sum);
	}
	return 0;
}

推到这里就可以自闭了。

d这个函数的前缀和不能杜教筛 也没有一个gcd的东西可以代替 如(d(i)=sum_{x=1}^{i}[(x,i)=x])这个东西没啥用 还是很难化简。

还要一个 (d(xy)=sum_{i|x}^{x}sum_{j|y}^{y}[(i,j)=1])

都跟本题没啥关系。

这道题之所以能做是因为 求的是前缀和这个特异性问题。

(d(w)=sum_{x|w}1) 观察这个东西可以发现(x,frac{w}{x})这个东西是一个二元组。

(sum_{i=1}^nsum_{p|i}d(p))观察这个东西 对于一个i来说 有(frac{i}{p},d(p))(frac{i}{p},frac{w}{x},x)

这个三元组会对答案贡献1.

可以发现 对于所有的三元组 xyz<=n 这样的三元组都会被计数。

且这个三元组是唯一存在于一个数字i内的。

设x<=y<=z.若x<=(n^{frac{1}{3}},yleq sqrt(frac{n}{x}))

枚举x,y统计z的数量即可。

由于x,y,z可以是一个排列所以要乘6.

但是观察上面的三元组的情况 发现对于x==y的时候这种情况是没有排列的所以需要减掉相应的次数 三个数字相等也同理。

非常妙的一道题。至于复杂度接近于(n^{frac{2}{3}})(我也不会证明。

const ll MAXN=100010;
ll n;
int main()
{
	freopen("divisor.in","r",stdin);
	freopen("divisor.out","w",stdout);
	get(n);
	ll w1=1,w2=1,ans=0;
	while(w1*w1*w1<=n)++w1;--w1;
	while(pf(w2)<=n)++w2;--w2;
	for(ll i=1;i<=w1;++i)
	{
		ll ww=n/i;
		for(ll j=i;j*j<=ww;++j)ans+=ww/j-j+1;
	}
	ll cnt=0;
	for(ll i=1;i<=w2;++i)cnt+=n/i/i;
	putl(ans*6-cnt*3-2*w1);
	return 0;
}
原文地址:https://www.cnblogs.com/chdy/p/12675665.html