此题太毒瘤了,以至于我单独为它写一篇题解。
-------------------
题目大意:任何自然数都可以用$2$的整数幂之和来表示。现在给定$T$组数据,每组一个$n$,求$n$用$2$的整数幂之和表示的方案数。$nleq 10^{18}$
首先科普一波伯努利数:
设自然数幂和$S_{k}(n)=sumlimits_{i=0}^{n-1} i^k$
将其用关于$n$的$m+1$次多项式表示,在其中定义伯努利数$B_i$为:
$S_k(n)=frac{1}{k+1}sumlimits_{i=0}^k C_{k+1}^i B_d n^{k+1-d}$
将$n=1$带入得:
$B_0=1 (m=0),sumlimits_{i=0}^k C_{k+1}^iB_i (m>0)$
于是可以$O(k^2)$求出$B_k$。
其正确性可以用生成函数证明。贴连接:https://www.cnblogs.com/JiaZP/p/13491011.html
首先一个普遍的做法应该能想到:设$f_n$表示$n$的方案数,那么通过打表有$f_n=sumlimits_{j=0}^{lceil frac{n}{2} ceil }f_j$。我们考虑转化一下这个式子:
$f_n=sumlimits_{j=0}^{lceil frac{n}{2} ceil }f_j$
$=sumlimits_{j=0}^{lfloor frac{n}{2} floor}sumlimits_{i=0}^{lfloor frac{j}{2} floor} f_i$
$=sumlimits_{i=0}^{lfloor frac{n}{4} floor} f_isumlimits_{j=2i}^{lfloor frac{n}{2} floor}1$
$=sumlimits_{i=0}^{lfloor frac{n}{4} floor} f_i(S_0(lfloor frac{n}{2} floor+1)-S_0(2i))$
$=sumlimits_{i=0}^{lfloor frac{n}{4} floor} f_iS_0(lfloor frac{n}{2} floor+1)-2sumlimits_{i=0}^{lfloor frac{n}{4} floor} f_i imes i$
发现前半部分很好解决,难的地方在于后面这个形如$sumlimits_{i=0}^n f_i imes i^k$的式子。我们尝试解这个式子:
设$F(n,k)=sumlimits_{i=0}^{n-1}i^k$
$sumlimits_{i=0}^n f_i imes i^k$
$=sumlimits_{i=0}^n i^ksumlimits_{j=0}^{lfloor frac{n}{2} floor}f_j$
$=sumlimits_{j=0}^{lfloor frac{n}{2} floor}f_jsumlimits_{i=2j}^n i^k$
$=sumlimits_{j=0}^{lfloor frac{n}{2} floor}f_j(S_k(n+1)-S_k(2j))$
$=sumlimits_{j=0}^{lfloor frac{n}{2} floor}f_jS_k(n+1)-sumlimits_{j=0}^{lfloor frac{n}{2} floor}f_jsumlimits_{d=1}^{k+1}frac{C_{k+1}^d}{k+1}B_{k+1-d}(2j)^d$
$=sumlimits_{j=0}^{lfloor frac{n}{2} floor}f_jS_k(n+1)-sumlimits_{d=1}^{k+1}frac{C_{k+1}^d}{k+1}B_{k+1-d}2^dsumlimits_{j=0}^{lfloor frac{n}{2} floor} f_j imes j^d$
$=F(lfloor frac{n}{2} floor,0)S_k(n+1)-sumlimits_{d=1}^{k+1}frac{C_{k+1}^d}{k+1}B_{k+1-d}2^dF(lfloor frac{n}{2} floor ,d)$
发现左边是子问题,右边$k$最多是$log n$。状态数$log^2 n$,转移$log n$,复杂度$O(Tlog^3 n)$。记忆化搜索实现。
(跟爆long long战斗了两个小时的我QAQ,码风可能有点丑,见谅
最好手写哈希表,用map复杂度多一个log,会被卡常。也可以用unordered_map
代码:
#include<bits/stdc++.h> #define int long long typedef long long ll; using namespace std; const int maxn=100; const int mod=1e9+7; int B[70],C[70][70],f[155][70],inv[70]; int T,n,tot; inline ll read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline int qpow(int x,int y) { int res=1; while(y) { if (y&1) res=res*x%mod; x=x*x%mod; y>>=1; } return res%mod; } inline void init() { const int up=66; for (int i=0;i<=up;i++) C[i][0]=1,inv[i]=qpow(i,mod-2); for (int i=1;i<=up;i++) for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]%mod+C[i-1][j]%mod)%mod; B[0]=1; for (int i=1;i<=up;i++) { for (int j=0;j<i;j++) B[i]=(B[i]%mod+(C[i+1][j]%mod*B[j]%mod)%mod)%mod; B[i]=(mod-(inv[i+1]%mod*B[i]%mod)%mod+mod)%mod; } } const int P=13331; struct h { long long cnt,head[P+1000]; struct edge{int next,to,val;}edge[maxn*2]; inline void init(){memset(head,0,sizeof(head));cnt=0;} inline void add(int to,int val) { int x=to%P+1; edge[++cnt].next=head[x]; edge[cnt].to=to; edge[cnt].val=val; head[x]=cnt; } inline int find(int v) { int x=v%P+1; for (int i=head[x];i;i=edge[i].next) { int to=edge[i].to; if (to==v) return edge[i].val; } return -1; } }Hash; inline int dp(ll n,int k) { int x=Hash.find(n); if (x==-1) Hash.add(n,++tot),x=tot; if (f[x][k]) return f[x][k]; if (n<=1) { if (n==0) return f[x][k]=(k==0?1:0); if (n==1) return f[x][k]=(k==0?2:1); } int res=0,tmp=(n+1)%mod; for (int d=1;d<=k+1;d++) { int tp=C[k+1][d]%mod; tp=(tp%mod*(inv[k+1]%mod))%mod; tp=(tp%mod*(B[k-d+1]%mod))%mod; tp=(tp%mod*tmp%mod)%mod; res=(res+tp)%mod; tmp=(n+1ll)%mod*tmp%mod; } res=(res%mod*dp(n>>1,0)%mod)%mod; for (int d=1;d<=k+1;d++) { int tmp=C[k+1][d]%mod; tmp=(tmp%mod*(inv[k+1]%mod))%mod; tmp=(tmp%mod*(B[k-d+1]%mod))%mod; tmp=(tmp%mod*((1ll<<d)%mod))%mod; tmp=(tmp%mod*(dp(n>>1,d)%mod))%mod; res=((res-tmp)%mod+mod)%mod; } return f[x][k]=res; } inline void work() { Hash.init(); tot=0; memset(f,0,sizeof(f)); printf("%lld ",(dp(n/2,0)%mod+mod)%mod); } signed main() { T=read(); init(); while(T--) { n=read(); work(); } return 0; }