洛谷B3903 [NICA #3] 星空(Hard Version) 题解
这道题很明显如果 ai中有大于x的数直接无解,输出0 。
因为ai都是2的整数次幂,所以不难看出每个ai在二进制表示下只会有一位上是1,那么,相邻的两个数相加,最多就是进一个位。
然后我们来考虑x假如 x的最高位1和次高位 1分别在i位j位上。由于没有大于x的数,所以现在a中最大的数也不会超过
2^i。我们来考虑这些数怎么放是符合要求的的:
- 很显然,等于2^i的数不能放一起,不然肯定不符合满足 ai+a(i+1)>x.
- 其他数彼此可以相邻,因为即使是2^(i-1)+2^(i-1)=2^i也小于等于x.
- 然后来考虑2^i和其他数的关系,发现只需要2^i与其他数中大于2^j的数不相邻即可.
将这些数进行分类
- - 设有number1个2^i:
- - 设有snumber2个2^t符合2^i<=2^t<=2^j;
- - 剩下称为number3;
设这些数没有排序需求,则全部是C,即组合运算;
根据排列组合的公式一定有:
number1!*number2!*number3!
不知道插板法的自己看:
(https://zhuanlan.zhihu.com/p/106135565
这时候我们要把number3作为挡板,number1作为所谓的“苹果”,插到板里于是就有了。
种方法
然后就是把number2插入剩下的空间了,这就非常简单了,因为还剩下number3+number2-number1个空间,要插入number3-sumber1个“苹果”要插入.
所以有
SO总可能数=*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