洛谷 P4317 花神的数论题 || bzoj3209
https://www.lydsy.com/JudgeOnline/problem.php?id=3209
https://www.luogu.org/problemnew/show/P4317
设cnt(x)为x在二进制下1的个数
很显然,要对于所有k,统计1<=i<=n中cnt(i)==k的i的个数
可以发现如果x二进制只由1组成,那么可以O(logx)计算出这些数
因此,可以把[1,n]用数位dp的思想拆开
对于n二进制中每一个1,试着使得它变为0,那么后面所有二进制位可以任意取,前面取的二进制位都是固定的,可以O(log)求这个范围内的贡献了
最后n自身要特判
调试记录:
1.尝试用求阶乘和阶乘逆元的方法直接求组合数,但是发现找不到如此大的质数,乘法也爆longlong麻烦;事实上在这里也的确比递推组合数麻烦得多
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 typedef pair<int,int> pii; 13 ll num[120],nt[120]; 14 ll n,m,fac[120],ifac[120],ans=1; 15 ll c[120][120]; 16 ll poww(ll a,ll b,ll md) 17 { 18 ll ans=1,base=a; 19 for(;b;base=base*base%md,b>>=1) 20 if(b&1) 21 ans=ans*base%md; 22 return ans; 23 } 24 void solve(ll n,ll *ans)//n位全1二进制答案 25 { 26 for(ll i=0;i<=n;i++) ans[i]=c[n][i]; 27 } 28 int main() 29 { 30 ll i,j,t; 31 for(i=0;i<=60;i++) c[i][0]=1; 32 for(i=1;i<=60;i++) 33 for(j=1;j<=i;j++) 34 c[i][j]=c[i-1][j]+c[i-1][j-1]; 35 scanf("%lld",&n);m=n; 36 m&=(~1LL); 37 t=__builtin_popcountll(m); 38 num[t]++; 39 if(m!=n) num[__builtin_popcountll(n)]++; 40 for(i=2;i<=60;i++) 41 { 42 m&=(~(1LL<<(i-1))); 43 t=__builtin_popcountll(m); 44 if(n&(1LL<<(i-1))) 45 { 46 solve(i-1,nt); 47 for(j=0;j<=i-1;j++) 48 num[j+t]+=nt[j]; 49 } 50 } 51 //for(i=1;i<=60;i++) printf("%lld %lld\n",i,num[i]); 52 for(i=1;i<=60;i++) ans=ans*poww(i,num[i],10000007)%10000007; 53 printf("%lld",ans); 54 return 0; 55 }