洛谷 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 }

 

posted @ 2018-07-24 22:14  hehe_54321  阅读(185)  评论(0编辑  收藏  举报
AmazingCounters.com