HDU4834 JZP Set 2014年百度之星程序设计大赛 - 初赛(第二轮) 数学

先分析一下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;
}


你可能感兴趣的:(HDU4834 JZP Set 2014年百度之星程序设计大赛 - 初赛(第二轮) 数学)