zoukankan      html  css  js  c++  java
  • 8月20日考试T3 题解(数论+动态规划)

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

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

    题目大意:任何自然数都可以用$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;
    }
  • 相关阅读:
    less-循环写法
    利用IntersectionObserver完成懒加载、加载更多
    通过is切换组件
    css3-背景渐变
    css-六边形、平行四边形、扇形实现
    vue-利用递归组件完成一个树形组件
    event loop 事件循环
    vue-组件间通信
    video 背景图平铺
    JS 事件委托
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13557247.html
Copyright © 2011-2022 走看看