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

avatar
avatar

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

直接做是nln的 可以简单转换一下 原式=\(\sum_{x=1}^n\frac{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}^n\sum_{p|i}d(p)\)观察这个东西 对于一个i来说 有\(\frac{i}{p},d(p)\)\(\frac{i}{p},\frac{w}{x},x\)

扫描二维码关注公众号,回复: 10666846 查看本文章

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

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

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

设x<=y<=z.若x<=\(n^{\frac{1}{3}},y\leq \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;
}

猜你喜欢

转载自www.cnblogs.com/chdy/p/12675665.html