bzoj 3209: 花神的数论题【数位dp】

参考:https://blog.csdn.net/sunshinezff/article/details/51049132
非典型数位dp
首先预处理,设f[i][j]为以0开头的i位数中1的个数为j的数的数量,g[i][j]为以1开头的i位数中1的个数为j的数的数量;转移是 f[i][j]=f[i-1][j]+g[i-1][j],g[i][j]=f[i-1][j-1]+g[i-1][j-1]
然后做数位dp,对于n在二进制下为1的位统计这样的1的个数出现过几次,然后快速幂即可

#include<iostream>
#include<cstdio>
using namespace std;
const int N=65,mod=10000007;
long long n,f[N][N],g[N][N];
long long ksm(long long a,long long b)
{
	long long r=1ll;
	while(b)
	{
		if(b&1)
			r=r*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return r;
}
int main()
{
	scanf("%lld",&n);
	f[1][0]=1,g[1][1]=1;
	for(int i=2;i<=60;i++)
		for(int j=0;j<=i;j++)
		{
			f[i][j]=f[i-1][j]+g[i-1][j];
			if(j>0)
				g[i][j]=f[i-1][j-1]+g[i-1][j-1];
		}
	long long t=0,c=0,ans=1;
	for(t=0;(1ll<<t)<=n;t++);
	for(;t;t--)
		if(1ll<<(t-1)&n)
		{
			for(int i=1;i<=t;i++)
				ans=ans*ksm(i+c,f[t][i])%mod;
			if(c)
				ans=ans*c%mod;
			c++;
		}
	printf("%lld\n",ans*c%mod);
	return 0;
}
posted @ 2018-07-30 18:42  lokiii  阅读(140)  评论(0编辑  收藏  举报