1. 程式人生 > >【題解】洛谷T48568[提高測試TG4]B.yyy送禮物 線性篩

【題解】洛谷T48568[提高測試TG4]B.yyy送禮物 線性篩

題目連結 在這裡插入圖片描述 在這裡插入圖片描述

簡單推導一下,可得每一項 nn 的答案為 n2i=1n(nii)n^2-\sum_{i=1}^n(\left\lfloor\frac{n}{i}\right\rfloor*i) 記數列 an=n2i=1n(nii)a_n=n^2-\sum_{i=1}^n(\left\lfloor\frac{n}{i}\right\rfloor*i) an1=(n1)2i=1n1(n1ii)a_{n-1}=(n-1)^2-\sum_{i=1}^{n-1}(\left\lfloor\frac{n-1}{i}\right\rfloor*i)

做差得遞推式 anan1=n1i=1n1[(nin1i)i]a_n-a_{n-1}=n-1-\sum_{i=1}^{n-1}\big[(\left\lfloor\frac{n}{i}\right\rfloor-\left\lfloor\frac{n-1}{i}\right\rfloor)*i\big] 思考一下,發現減去的那一坨就是 nn 的因子和減去 nn,於是可以線性遞推了。 因子和可以線性篩方便求出。 (好像做麻煩了……)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e7+10;
ll ans[N];
int prime[N/10],p,sum[N];
bool iscomp[N];
int read()
{
	int s=0,t=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')t=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*t;
}
int t,n;
ll qpow(ll a,int b)
{
	ll ret=1;
	for(;b;b>>=1)
	{
		if(b&1)ret*=a;
		a*=a;
	}
	return ret;
}
void primetable()
{
	sum[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!iscomp[i])prime[p++]=i,sum[i]=1+i;
		for(int j=0;j<p&&i*prime[j]<N;j++)
		{
			iscomp[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				int tmp=i,f=1;
				while(tmp%prime[j]==0)tmp/=prime[j],f++;
			    sum[i*prime[j]]=sum[tmp]*(qpow(prime[j],f+1)-1)/(prime[j]-1);
			    break;
			}
			sum[i*prime[j]]=sum[i]*sum[prime[j]];
		}
	}
}
int main()
{
	primetable();
	for(int i=2;i<N;i++)
	    ans[i]=0ll+ans[i-1]+i-1-(sum[i]-i);
	t=read();
	while(t--)
	{
		n=read();
		printf("%lld\n",ans[n]);
	}
	return 0;
}

總結

挺好的推公式題。線性篩確實可以輕鬆解決不少問題。