洛谷B3903 [NICA #3] 星空(Hard Version) 题解

洛谷B3903 [NICA #3] 星空(Hard Version) 题解

这道题很明显如果 ai中有大于x的数直接无解,输出0 。

因为ai都是2的整数次幂,所以不难看出每个ai在二进制表示下只会有一位上是1,那么,相邻的两个数相加,最多就是进一个位。
然后我们来考虑x假如 x的最高位1和次高位 1分别在i位j位上。由于没有大于x的数,所以现在a中最大的数也不会超过 
2^i。我们来考虑这些数怎么放是符合要求的的:

  1.  很显然,等于2^i的数不能放一起,不然肯定不符合满足 ai+a(i+1)>x.
  2. 其他数彼此可以相邻,因为即使是2^(i-1)+2^(i-1)=2^i也小于等于x.
  3. 然后来考虑2^i和其他数的关系,发现只需要2^i与其他数中大于2^j的数不相邻即可.

将这些数进行分类

  1. - 设有number1个2^i:
  2. - 设有snumber2个2^t符合2^i<=2^t<=2^j;
  3. - 剩下称为number3;

设这些数没有排序需求,则全部是C,即组合运算;
根据排列组合的公式一定有:

number1!*number2!*number3!

不知道插板法的自己看:

(https://zhuanlan.zhihu.com/p/106135565

这时候我们要把number3作为挡板,number1作为所谓的“苹果”,插到板里于是就有了。

\binom{number3+1}{ number1}种方法

然后就是把number2插入剩下的空间了,这就非常简单了,因为还剩下number3+number2-number1个空间,要插入number3-sumber1个“苹果”要插入.

所以有
\binom{number3+number2-number1}{number3-sumber1}

SO总可能数=
\binom{number3+1 }{ number1}*\binom{number3+number2-number1}{number3-sumber1}*number3!*number1!*number2!

#include<iostream>
#include<fstream>
#include <vector>
#include <iomanip>
#include <bits/stdc++.h>
#include <math.h>
#include <algorithm>
#include <cstring>
#include<bits/stdc++.h>
namespace A
 {
    int read()
    {
        int x=0,flag=1;
        char zifu=getchar();
        while(zifu<'0'||zifu>'9'){
            if(zifu=='-'){
                flag=-1;
            }
            zifu=getchar();
        }
        while(zifu>='0'&&zifu<='9'){
            x*=10;x+=zifu-48;
            zifu=getchar();
        }     
        return x*flag;
    }
}
using A::read;
#define ll long long
using namespace std;
const int N=1e5+100;
int f[N],v[N];
ll n,X;
ll a[N];
const int mod=1e9+7;
int C(int n,int m){
    if(n==m) return 1;
    if(n<0||m<0) return 0;
    if(n<m) return 0;
    return 1ll*f[n]*v[m]%mod*v[n-m]%mod;
}
int fpow(int a,int b) {
    a%=mod;
    int r=1;
    while(b){
        if(b&1){
            r=1ll*r*a%mod;
        }
        b>>=1;
        a=1ll*a*a%mod;
    }
    return r;
}
int number1,number2;
int hi[N];
 int main(){
    scanf("%lld%lld",&n,&X);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        for(int j=63;j>=1;--j){
            if((a[i]>>(j-1))&1){
                hi[i]=j;
                break;
            }
        }
    }
    f[0]=1;
    for(int i=1;i<=n+2;++i){
        f[i]=1ll*f[i-1]*i%mod;
    }
    v[n+2]=fpow(f[n+2],mod-2);
    for(int i=n+1;i>=0;--i)
    {
        v[i]=1ll*v[i+1]*(i+1)%mod;
    }
    for(int i=1;i<=n;++i){
        if(a[i]>X){
            puts("0");
            return 0;
        }
    } 
    for(int i=63;i>=1;--i){
        if((X>>(i-1))&1){
            if(!number1){
                number1=i;
            }else if(!number2) {
                number2=i;
            }
        }
    }
    int c1=0,c2=0,c3=0;
    for(int i=1;i<=n;++i)
    {
        if(hi[i]==number1)
        {
            ++c1;
        }
        else if(hi[i]>number2)
        {
            ++c2;
        }
        else ++c3;
    }
    int ans=1ll*f[c1]*f[c2]%mod*f[c3]%mod*C(c3+1,c1)%mod*C(c2+c3-c1,c3-c1)%mod;
    printf("%d",ans);
    return 0;
}
最后附上AC记录和题目链接

AC记录https://www.luogu.com.cn/record/159516104

洛谷题目链接https://www.luogu.com.cn/problem/B3903

有点小长,但还阔以,呵呵
 
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值