SPOJ - INTSUB 组合数学

题意:

给你一个集合{1,2,3,........,2n},问有多少个子集是有趣的。有趣的集合定义是,集合中两个不一样的数{a,b},其中a是集合里的最小值,b是a的倍数。

题解:

对于数字a,集合中存在x=2n/a-1个大于a的倍数,也存在y=2n-x-a个大于a但不是a的倍数的数。对于有趣的集合必须要出席那大于a的倍数,一共有2^x-1种可能(减去一个都不出现的情况),而不是倍数的数可出现可不出现,一共有2^y种可能,将两个值相乘,即为最小值为a的有趣的子集的个数。枚举所有a的情况,相加即为答案。


#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const LL MOD=1000000007;
LL pr[2005];
int main(){
    LL T,n;
    pr[0]=1;
    for(int i=1;i<=2000;i++)
        pr[i]=pr[i-1]*2%MOD;
    scanf("%lld",&T);
    for(LL t=1;t<=T;t++){
        scanf("%lld",&n);
        LL ans=0;
        for(LL i=1;i<=n;i++){
            LL x=2*n/i-1;
            LL y=2*n-i-x;
            ans=(ans+(pr[x]-1)*pr[y])%MOD;
        }
        printf("Case %lld: %lld\n",t,ans);
    }
}

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const LL MOD=1000000007;
LL po(LL x){
    LL ret=1,a=2;
    while(x){
        if(x&1) ret=ret*a%MOD;
        a=a*a%MOD;
        x>>=1;
    }
    return ret;
}
int main(){
    LL T,n;
    scanf("%lld",&T);
    for(LL t=1;t<=T;t++){
        scanf("%lld",&n);
        LL ans=0;
        for(LL i=1;i<=n;i++){
            LL x=2*n/i-1;
            LL y=2*n-i-x;
            ans=(ans+(po(x)-1)*po(y))%MOD;
        }
        printf("Case %lld: %lld\n",t,ans);
    }
}

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/79980149
个人分类: 数论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭