先分析一下JZP Set究竟是什么。
我们考虑一个JZP Set内连续三项a、b、c,考虑它们的间隔,记d1=b-a,d2=c-b,如果d1或d2为偶数,那么对应的两个数的平均数不在这个集合内,该集合不是JZP Set。如果d1和d2都是奇数,但b!=(a+c)/2,那么a、c两项的平均数也不在这个集合内,该集合也不是JZP Set。所以JZP Set内的数必构成以奇数为公差的等差数列。
考虑如何计数,显然空集只有一个,单元素集有n个,那么公差为1的等差数列有(n-1)+(n-2)+(n-3)+...+1(为了方便,这里把两项的数列也叫做等差数列),公差为3的等差数列有(n-3)+(n-6)+(n-9)+...+(n-3*k)(这里k满足3*k<n,3*(k+1)>=n),依此类推。
记f(i)为i的奇约数个数,能够得到,数字p的上述的式子中,被减去了f(p)次。记s1(i)为f(i)的前缀和,s2(i)为f(i)*i的前缀和,这样我们得到n在上述式子中被加了s1(n-1)+1次,减去数的总和为s2(n-1)。
用线性筛法计算f(i),可以做到O(n)预处理+O(1)询问。
//HDU4834 #include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cstring> using namespace std; const int MAXN=10000010; bool nprime[MAXN]; int prime[750000],tot,dnum[MAXN],tms[MAXN],tms2[MAXN]; int s1[MAXN]; long long s2[MAXN]; int n,m; long long ans; void getPrime(int n) { for(int i=2;i<=n;i++) { if(!nprime[i]) {prime[++tot]=i,dnum[i]=2,tms[i]=1;} for(int j=1;(j<=tot)&&(i*prime[j]<=n);j++) { nprime[i*prime[j]]=true; if(i%prime[j]==0) { dnum[i*prime[j]]=dnum[i]/(tms[i]+1)*(tms[i]+2); tms[i*prime[j]]=tms[i]+1; break; } dnum[i*prime[j]]=dnum[i]*dnum[prime[j]]; tms[i*prime[j]]=1; } } } int main() { getPrime(10000000); tms2[1]=0,tms2[2]=1,dnum[1]=1; for(int i=3;i<=10000000;i++) if(!(i&1)) tms2[i]=tms2[i>>1]+1; for(int i=1;i<=10000000;i++) dnum[i]/=tms2[i]+1,s1[i]=s1[i-1]+(long long)dnum[i],s2[i]=s2[i-1]+(long long)i*dnum[i]; scanf("%d",&m); for(int cas=1;cas<=m;cas++) { scanf("%d",&n); ans=n+1+(long long)s1[n-1]*n-s2[n-1]; printf("Case #%d:\n",cas); printf("%I64d\n",ans); } return 0; }