• 题目分享I 二代目


    题意:找有多少对uv满足存在两个整数ab使得a+b=u,a^b=v (^是异或)

    分析:不难想到dp,但如何求状态转移方程呢?

    这其实可以理解为数位dp(?)

    首先有一点性质是a^b≤a+b,

    这个很容易理解,因为当他们对应二进制位同为0时,a^b=a+b=0

    当他们对应二进制位同为1时,a^b=0而a+b=2,也就是a+b>a^b

    当他们对应二进制位分别为01时,a^b=a+b=1

    显然a+b=(a&b)*2+a^b,这个式子后面也有点用,但现在主要就是这个a^b≤a+b这个式子

    那么因为这个式子的存在,所以v≤u,也就是说我们可以令dp[i]表示u不超过i的uv数对的个数,当然也可以表示为a+b≤n,且a^b与a+b的数对的个数

    首先这里就有个小问题就是,如果我们按照dp[i]从dp[i-1]推过来,显然先不说空间会不会超,因为有可能可以压,但时间必定会超

    所以这里要用一点数位的思想

    那么还是先把比较结论性的东西说一下,再把比如为什么这么做不那么做之类的问题解决一下

    首先设a=ap*2+aq(aq=0或1) b=bp*2+bq (bq=0或1)(这样就做到了类似数位的思想)

    那么u=a+b≤n,也就是(ap+bp)*2+(aq+bq)≤n

    稍微变一下就是

    ap+bp≤(n-aq-bq)/2

    这里的aq与bq只能取0或1

    所以当aq+bq=0时,ap+bp≤n/2 叫他方案A吧

    当aq+bq=1时,ap+bp≤(n-1)/2 叫他方案B吧

    当aq+bq=2时,ap+bp≤(n-2)/2 叫他方案C吧

    那么,dp[n]其实就是这三种可能情况下方案数的总和(不能有重复),注意这里的方案数是(a+b,a^b)的方案数

    (a+b,a^b)带入一下就是((ap+bp)*2+aq+bq,(ap^bp)*2+aq^bq)

    首先,这三种类别之间是不会有重复的(a+b,a^b)产生

    首先方案A,C与方案B显然不会重合,因为他们a+b%2不同,所以a+b自然也不会相同

    那么重点就是A与C为什么不会重合,先说一下为什么好像可能会重合,因为他们a+b%2都是0

    那么什么情况下他们会重合呢?

    ap+bp=ap'+bp'+1 且 ap^bp=ap'^bp' 时 会出现重合,那么这种情况存在吗?

    答案是显然不存在

    你可以带入上面那个有点用的式子

    a+b=(a&b)*2+a^b

    显然稍微消一下就能得到 (ap&bp)*2=(ap'&bp')*2+1 这显然是不可能的

    所以现在我们证明了这三种方案是不重合的

    然后就是保证这三种方案内部不重合的方案数如何求出了

    其实也很简单,因为他们的末尾已经知道了,所以原来要求的(a+b,a^b)不重复其实就变成了(ap+bp,ap^bp)不重复了

    那么也就是说上面三种方案可以直接改写成下面这样

    A:dp[n/2]

    B:dp[(n-1)/2]

    C:dp[(n-2)/2]

    所以dp[n]=dp[n/2]+dp[(n-1)/2]+dp[(n-2)/2]

    式子总算推完了,然后就是一些小问题

    比如,这显然是要用记忆化,但这样会不会超空间以及超时间呢?

    这其实也很容易推,n/2,(n-1)/2,(n-2)/2显然会有两种相差为1的结果,可以设为x,x-1

    这两种结果又有x/2,(x-1)/2,(x-2)/2,(x-3)/2,也就是2或3种结果,如果是3种结果的话,设为x,x-1,x-2

    那么就有x/2,(x-1)/2,(x-2)/2,(x-3)/2,(x-4)/2,也就是3种结果。。。。。。

    所以其实dp[n]最多也就是2+2+3+3+3+3...只需要lgn*3个也就是最多180多个

    但他们的下标却是ll级别的,所以要用map

    还有一个问题就是,为什么要用二进制,比如用三进制,aq+bq分0,1,2,3,4一共5类不行吗?

    不行,因为只能从a+b,a^b得到ap+bp,ap^bp(二进制),而得不到三进制的结论

    代码:

    #include<cstdio>
    #include<map>
    using namespace std;
    
    #define ll long long
    
    const int mod=1e9+7;
    map<ll,ll> dp;
    
    ll dfs(ll x)
    {
        if(dp[x]) return dp[x];return dp[x]=(dfs(x/2)+dfs((x-1)/2)+dfs((x-2)/2))%mod;
    }
    
    int main()
    {
        ll n;
        scanf("%lld",&n);
        dp[0]=1ll;dp[1]=2ll;
        printf("%lld",dfs(n));
        return 0;
    }
  • 相关阅读:
    使用weave管理docker网络
    为docker配置固定ip
    Building good docker images
    使用curl命令获取文件下载速度
    吐槽Java
    Kubernetes 中的服务发现与负载均衡(转)
    Kubernetes系列之介绍篇(转)
    top命令中的wa指标(转)
    uwsgi常用参数详解(转)
    Unix域套接字-Unix Domain Socket(转)
  • 原文地址:https://www.cnblogs.com/lin4xu/p/12864633.html
Copyright © 2020-2023  润新知