8月20日考试T3 题解(数论+动态规划)

此题太毒瘤了,以至于我单独为它写一篇题解。

-------------------

题目大意:任何自然数都可以用$2$的整数幂之和来表示。现在给定$T$组数据,每组一个$n$,求$n$用$2$的整数幂之和表示的方案数。$n\leq 10^{18}$

首先科普一波伯努利数:

设自然数幂和$S_{k}(n)=\sum\limits_{i=0}^{n-1} i^k$

将其用关于$n$的$m+1$次多项式表示,在其中定义伯努利数$B_i$为:

$S_k(n)=\frac{1}{k+1}\sum\limits_{i=0}^k C_{k+1}^i B_d n^{k+1-d}$

将$n=1$带入得:

$B_0=1\ (m=0),\sum\limits_{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=\sum\limits_{j=0}^{\lceil \frac{n}{2} \rceil }f_j$。我们考虑转化一下这个式子:

$f_n=\sum\limits_{j=0}^{\lceil \frac{n}{2} \rceil }f_j$

$=\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}\sum\limits_{i=0}^{\lfloor \frac{j}{2} \rfloor} f_i$

$=\sum\limits_{i=0}^{\lfloor \frac{n}{4} \rfloor} f_i\sum\limits_{j=2i}^{\lfloor \frac{n}{2} \rfloor}1$

$=\sum\limits_{i=0}^{\lfloor \frac{n}{4} \rfloor} f_i(S_0(\lfloor \frac{n}{2} \rfloor+1)-S_0(2i))$

$=\sum\limits_{i=0}^{\lfloor \frac{n}{4} \rfloor} f_iS_0(\lfloor \frac{n}{2} \rfloor+1)-2\sum\limits_{i=0}^{\lfloor \frac{n}{4} \rfloor} f_i\times i$

发现前半部分很好解决,难的地方在于后面这个形如$\sum\limits_{i=0}^n f_i \times i^k$的式子。我们尝试解这个式子:

 设$F(n,k)=\sum\limits_{i=0}^{n-1}i^k$

$\sum\limits_{i=0}^n f_i \times i^k$

$=\sum\limits_{i=0}^n i^k\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_j$

$=\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_j\sum\limits_{i=2j}^n i^k$

$=\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_j(S_k(n+1)-S_k(2j))$

$=\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_jS_k(n+1)-\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_j\sum\limits_{d=1}^{k+1}\frac{C_{k+1}^d}{k+1}B_{k+1-d}(2j)^d$

$=\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor}f_jS_k(n+1)-\sum\limits_{d=1}^{k+1}\frac{C_{k+1}^d}{k+1}B_{k+1-d}2^d\sum\limits_{j=0}^{\lfloor \frac{n}{2} \rfloor} f_j \times j^d$

$=F(\lfloor \frac{n}{2} \rfloor,0)S_k(n+1)-\sum\limits_{d=1}^{k+1}\frac{C_{k+1}^d}{k+1}B_{k+1-d}2^dF(\lfloor \frac{n}{2} \rfloor ,d)$

发现左边是子问题,右边$k$最多是$\log n$。状态数$\log^2 n$,转移$\log n$,复杂度$O(T\log^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\n",(dp(n/2,0)%mod+mod)%mod);
}
signed main()
{
    T=read();
    init();
    while(T--)
    {
        n=read();
        work();
    }
    return 0;
}

 

posted @ 2020-08-24 23:43  我亦如此向往  阅读(165)  评论(0编辑  收藏  举报