• The 2022 CCPC Guangzhou Onsite M. XOR Sum(数位dp 数位背包)


    题目

    给定n,m,k(0<=n<=1e15,0<=m<=1e12,1<=k<=18),

    求长度为k的数组a,ai为[0,m]的整数,

    满足i=1kj=1i1aiaj=n" role="presentation" style="position: relative;">i=1kj=1i1aiaj=n的方案数

    答案对1e9+7取模

    题解

    第一反应想起了hdu3693,但比对了一下,感觉那个题难很多,

    两年前写的题忘了写题解,现在不会了

    Educational Codeforces Round 104 (Rated for Div. 2) F.Ones(数位dp)_Code92007的博客-CSDN博客

    后来想了想,感觉更像这个题,按余数统计方案数,

    控制余数在一定范围,姑且称它为数位背包

    每一位的贡献可以独立考虑,而这个式子实际是(i,j)对的01值不同就会产生贡献,

    所以只用关注当前几个填1几个填0,由于有的卡上界有的不卡,所以记录一下当前有几个卡上界

    dp[i][j][l]表示当前从高到低考虑到低i位时,只考虑n的i位及更高位时的数的当前余数是j,选的l个数卡上界的方案数

    k=18时,最大贡献是9*9=81,考虑当前n的余数x最大能是多少,

    (x-81)*2

    所以转移过程中可以忽略>162的转移,因为通过更低的位,再也不能使其归0

    而<0的转移也不可取,

    因为这个操作,转移到下一位,相当于*2,下一位n有余数,相当于+1,

    还有减去当前选的值,相当于减去一个非负数,而(-1)*2+1还为-1,最终不能变为0

    所以,可以只关注[0,162)的转移,代码中写的是[0,256)

    此外,初始局面时,n的余数超过256也显然无解

    转移分两种情况讨论,当前位为0 或者 当前位为1

    1. 当前位为0,卡上界的只能选0,for枚举不卡上界的数中选了x个1

    2. 当前位为1,for枚举卡上界的数中选了x个1,for枚举不卡上界的数中选了y个1

    选出这些数来,从而确定了贡献数对的数量,也确定了下一次卡上界的数的个数,

    并算出下一位的n的当前余数,递归到子状态搜索即可

    复杂度O(40*256*18*18*18),但跑不满

    注意特判k=1(取不出两个数)和m=0(a数组为空)的情况

    代码

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=42,M=256,K=19,mod=1e9+7;
    5. ll n,m,dp[N][M][K],c[K][K];
    6. int k,a[N];
    7. ll dfs(int x,int r,int cur){
    8. if(r>=M)return 0;
    9. if(x==-1)return r==0;
    10. if(~dp[x][r][cur])return dp[x][r][cur];
    11. ll &ans=dp[x][r][cur];ans=0;
    12. int nb=(x==0?0:(n>>(x-1)&1));
    13. //printf("x:%d ax:%d\n",x,a[x]);
    14. if(a[x]==0){
    15. for(int i=0;i<=k-cur;++i){
    16. int nr=r-i*(k-i);
    17. if(nr<0)continue;
    18. ans=(ans+1ll*c[k-cur][i]*dfs(x-1,(nr<<1)|nb,cur)%mod)%mod;
    19. }
    20. }
    21. else{
    22. for(int i=0;i<=cur;++i){
    23. for(int j=0;j<=k-cur;++j){
    24. int nr=r-(i+j)*(k-(i+j));
    25. //printf("x:%d r:%d cur:%d i:%d j:%d nr:%d\n",x,r,cur,i,j,nr);
    26. if(nr<0)continue;
    27. ans=(ans+1ll*c[cur][i]*c[k-cur][j]%mod*dfs(x-1,(nr<<1)|nb,i)%mod)%mod;
    28. }
    29. }
    30. }
    31. //printf("x:%d r:%d cur:%d ans:%lld\n",x,r,cur,ans);
    32. return ans;
    33. }
    34. ll cal(){
    35. if(k==1)return !n;
    36. if(!m)return !n;
    37. memset(dp,-1,sizeof dp);
    38. int c=0;
    39. for(ll x=m;x;x>>=1){
    40. a[c++]=x%2;
    41. }
    42. ll rn=0;
    43. for(int i=c;i<=50;++i){
    44. if(n>>i&1){
    45. rn+=1ll<<(i-c);
    46. if(rn>=M)return 0;
    47. }
    48. }
    49. return dfs(c,(int)rn,k);
    50. }
    51. int main(){
    52. c[0][0]=1;
    53. for(int i=1;i
    54. c[i][0]=c[i][i]=1;
    55. for(int j=1;j
    56. c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    57. }
    58. }
    59. cin>>n>>m>>k;
    60. cout<<cal()<
    61. return 0;
    62. }

  • 相关阅读:
    客户端存储localStorage和sessionStorage以及Cookie
    新编译原理的草稿
    Java笔记:手写spring之ioc
    钟汉良日记:你相信神话吗?你会背《正气歌》吗
    RHEL - 订阅、注册系统和 Yum Repository
    【LeetCode 每日一题】53. 最大子数组和
    【Rancher】Rancher 2.6.x 搭建Kubernetes集群
    【web前端特效源码】使用HTML5& CSS&Js实现倒计时数字
    IM即时通讯开发iOS多设备字体适配方案
    Vinco Ventures任命Ted Farnsworth为联合首席执行官
  • 原文地址:https://blog.csdn.net/Code92007/article/details/127840174