JZP Set

Problem Description
一个{1, ..., n}的子集S被称为JZP集,当且仅当对于任意S中的两个数x,y,若(x+y)/2为整数,那么(x+y)/2也属于S。
例如,n=3,S={1,3}不是JZP集,因为(1+3)/2=2不属于S。但是{1,2,3}的其他子集都属于S,所以n=3时有7个JZP集
给定n,求JZP集的个数。
 

Input
第一行为T,表示输入数据组数。
每组数据包含一行整数n。

限制条件
1<=T<=10^5
1<=n<=10^7
 

Output
对第i组数据,输出
Case #i:
然后输出JZP集的个数。
 

Sample Input
  
  
3 1 2 3
 

Sample Output
  
  
Case #1: 2 Case #2: 4 Case #3: 7


百度之星的初赛最后一题。

这题在比赛的时候没能弄出来。开始经过推到,发现a【n】=a【n-1】+cnt;始终不知道这个cnt怎么得到;

经过手动推到会发现这个集合的数只有一个数的时候满足,当这里面的数字是出现等差数列的时候同样满足,而等差数列的公差一定为奇数。这是当是相当的,那么以后的工作就稍微简单一点了,这就需要求出cnt。具体求发参照大神的题解:

 http://www.cnblogs.com/oyking/p/3751608.html

那么令p[i] = 包含i的[1..i]的JZP子集个数。考虑包含i的等差数列,可以发现等差一定为奇数(偶数都无法构成JZP集合,手动试一下就能发现了)。
首先p[i]有只包含一个数的{i}。
之后对于元素个数大于等于2的,等差为1的子集个数为(i-1)/1,等差为3的子集个数为(i-1)/3……
但是这还不能满足题目的要求。
考虑p[i]-1和p[i-1]-1的区别,对于(i-1)/1+(i-1)/3+(i-1)/5……和(i-2)/1+(i-2)/3+(i-2)/5……,对于分母为x的,(i-1) / x > (i-2) / x当且仅当(i-1)是x的倍数
可以得出p[i]比p[i-1]要大count(i-1的奇数约数)。
对于每个数的约数个数,可以在O(nlogn)的时间里预处理出来(参考素数的筛法)。
然后递推ans[i] = ans[i - 1] + p[i],至此题目完美解决!


#include<iostream>
#include<fstream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<sstream>
#include<cassert>
using namespace std;

#define LL __int64
//#define LL long long
#define maxn 10000007


int n;

LL a[maxn];
LL p[maxn];

void init() {
    for(int i=1; i<=maxn; i+=2) {
        for(int j=i; j<=maxn; j+=i) {
            p[j]++;
        }
    }

    a[1]=2;
    LL sum=0;
    for(int i=2; i<maxn; i++) {
        sum+=p[i-1];
        a[i]=a[i-1]+sum+1;
    }
}

int main() {
    int t;
    memset(p,0,sizeof(p));
    init();
    cin>>t;
    for(int ii=1; ii<=t; ii++) {
        scanf("%d",&n);
        printf("Case #%d:\n",ii);
        printf("%I64d\n",a[n]);
    }
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值