zoukankan      html  css  js  c++  java
  • POJ 3252 组合数学?

    大神们的题解我一个都没看懂。。。。。。。。。。。

    十分的尴尬
    题意:算出闭区间内二进制中0的个数大于等于1的个数的数字有多少个
    思路:
    组合数学(n小于500的时候都可以出解,只不过高精比较麻烦)。
    这道题还算比较仁慈。。。

    Discuss里面有一段说得挺好的

    看完各家算法,尝试独立分析一下: 以sample为例子
    [2,12]区间的RoundNumbers(简称RN)个数:Rn[2,12]=Rn[0,12]-Rn[0,1]
    即:Rn[start,finish]=Rn[0,finish]-Rn[0,start-1] 所以关键是给定一个X,求出Rn[0,X]
    现在假设X=10100100 这个X的二进制总共是8位,任何一个小于8位的二进制都小于X
    第一部分,求出长度为[0,7]区间内的二进制是RoundNumber的个数
    对于一个长度为Len的二进制(最高位为1),如何求出他的RoundNumbers呢(假设为用R(len)来表达),分为奇数和偶数两种情况
    1、奇数情况:在Len=2k+1的情况下,最高位为1,剩下2k位,至少需要k+1为0
    用C(m,n)表示排列组合数:从m个位置选出n个位置的方法
    R(len)=C(2k,k+1)+C(2k,k+2)+…+C(2k,2k). 由于
    A:C(2k,0)+C(2k,1)+…+C(2k,2k)=2^(2k)
    B:C(2k,0)=C(2k,2k), C(2k,1)=C(2k,2k-1) ,,C(2k,i)=C(2k,2k-i) 于是 C(2k,0)+C(2k,1)+…+C(2k,2k)
    = C(2k,0)+C(2k,1)+…+C(2k,k)+C(2k,k+1)+C(2k,K+2)+…+C(2k,2k)
    = 2*R(len)+C(2k,k)
    =2^(2k)
    所以R(len)=1/2*{2^(2k)-C(2k,k)};
    2. 偶数情况 len=2*k,类似可以推到 R(len)=1/2*(2^(2k-1)); 第二部分,对于上面这个长度为8的例子:即X=10100100,首先如果本身是RoundNumbers,第二部分的结果总数+1
    第一部分已经将长度小于8的部分求出。现在要求长度=8的RoundNumber数目 长度为8,所以第一个1不可改变
    现在到第二个1,如果Y是前缀如100*****的二进制,这个前缀下,后面取0和1必然小于X,已经有2个0,一个1,剩下的5个数字中至少需要2个0,
    所以把第二个1改为0:可以有C(5,2)+C(5,3)+C(5,4)+C(5,5)
    现在第三个1,也就是前最为101000**,同样求出,至少需要0个0就可,所以有C(2,0)+C(2,1)+C(2,2)个RoundNumbers
    。。。
    将所有除了第一个1以外的1全部变为0,如上算出有多少个RoundNumbers,结果相加(由于前缀不一样,所以后面不管怎么组合都是唯一的)

    将第一部分和第二部分的结果相加,就是最后的结果了。 精度要求方面,用int就可以了:two
    billion=20亿<2*1024*1024*1024=2^31,需用31位来表示数组,由于第一位总是1,所以求组合数的时候最多求30,C(30,k),k取值区间是[0,30],因为C(k,i)<2^k,所以结果用int表示就可以

    (也有人用数位DP、记忆化搜索什么的。。。。 看个人喜好吧)

    G++ AC C++WA 鬼知道为什么。。。

    // by SiriusRen
    #include <bitset>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    bitset<64>n,m;
    int C[35][35];
    int N,M,maxn=0,maxm=0,cnt1=1,cnt0=0;
    int ans=0;
    int main()
    {
        scanf("%d%d",&N,&M);M++;
        for(int i=30;i>=0;i--){
            if(M&(1<<i))m[i]=1,maxm=max(maxm,i);
            if(N&(1<<i))n[i]=1,maxn=max(maxn,i);
        }
        for(int i=0;i<=31;i++)C[i][0]=1;
        for(int i=1;i<=31;i++)
            for(int j=1;j<=i;j++)
                C[i][j]=C[i-1][j-1]+C[i-1][j];
        for(int i=maxm;i>=0;i--)
            for(int j=i-1;2*j>=i;j--)ans+=C[i-1][j];
        for(int i=maxm-1;i>=0;i--)
            if(m[i]){
                for(int j=maxm-cnt0-cnt1;2*j+2*cnt0+1>=maxm;j--)
                    ans+=C[maxm-cnt0-cnt1][j];
                cnt1++;
            }
            else cnt0++;
        cnt1=1;cnt0=0;
        for(int i=maxn;i>=0;i--)
            for(int j=i-1;2*j>=i;j--)ans-=C[i-1][j];
        for(int i=maxn-1;i>=0;i--)
            if(n[i]){
                for(int j=maxn-cnt0-cnt1;2*j+2*cnt0+1>=maxn;j--)
                    ans-=C[maxn-cnt0-cnt1][j];
                cnt1++;
            }
            else cnt0++;
        printf("%d
    ",ans);
    }

    这里写图片描述

    看到另一份题解里有这样一句话:
    组合数学,一跪一天,爽!

    表示赞同

  • 相关阅读:
    ArcGIS进行视域分析及地形图制作
    ArcGIS进行容积率计算
    ArcGIS对进行数据拓扑修改
    如何打开软键盘
    China一词的由来
    名侦探柯南剧集数据统计分析
    常用QQ快捷键
    福利|GISer需知网站
    中国程序员最容易读错的单词
    截取数组
  • 原文地址:https://www.cnblogs.com/SiriusRen/p/6532364.html
Copyright © 2011-2022 走看看