首页 > 其他分享 >花神的数论题(数位dp)

花神的数论题(数位dp)

时间:2024-02-28 22:00:11浏览次数:35  
标签:二进制位 res ll 花神 int ans dp 数位

花神的数论题

题目描述

设 \(\text{sum}(i)\) 表示 \(i\) 的二进制表示中 \(1\) 的个数。给出一个正整数 \(N\) ,花神要问你 \(\prod_{i=1}^{N}\text{sum}(i)\) ,也就是 \(\text{sum}(1)\sim\text{sum}(N)\) 的乘积。

数据范围

\(1\le N\le 10^{15}\)。

解法

首先我们要想到,把n表示成二进制位后,就可以从高位往低位做了,此时我们只需要处理单个的二进制位的\(sum(i)\),然后算的时候再加上比他大的二进制位中1的个数即可。

所以现在问题转化成了,对于一个2的次幂,我们如何快速计算\(sum(i)\)。通过打表我们好像并没法一眼看出什么规律,于是我们直接从其原本意义开始想起。对于sum(8),我们发现从1到7时前三位二进制,是从001(1)变成111(7)。想到组合数学,C(k,i)代表k位中有i个1的数量。而当二进制变为1000……的时候,其方案数也正好对应C(k,0)。所以对于n的每一个为1的二进制位,我们计算\(\prod_{i=1}^{k}{(i+res)}^{C(k,i)}\)。这个公式中,k表示这个二进制位数减一,res表示之前已经比该位大且为1的二进制位数。

出题人很好玩地把模数设为了10000007 ,痛wa两发。

然后看了题解,发现小粉兔的数位dp显然更优美。我们设dp[i]为1的个数为i的总次数,然后进行dp。第一维枚举二进制位,第二维枚举次数。转移式子为dp[i]+=dp[i-1]。转移完毕后如果当前二进制位的个数是j且该位为1,那么就dp[j-1]++。

我们来想一下其正确性。结合我们在上面提到的做法,不难理解转移后让dp[j-1]++,表示为还没加进来这个二进制位结尾。重点在于dp转移。我们会发现,第x位会在第倒数x次开始起到贡献,那么他的加入会使得每一个dp[i-1]背后的那个二进制加上他后变成dp[i],并且只会从dp[j]开始起效果(因为轮到这个二进制位的时候,前面更大的1是不动的),并且只会起效果x次(这是比较对的,因为这一位最大也就只能转移到它原本就在的位置上)。

小粉土牛逼

代码实现

我自己的思路

const int mod=1e7+7;
ll binpow(ll a,ll b,ll c){
    ll ans=1;
    while(b){
        if(b&1)
        ans=(Int)ans*a%c;
        a=(Int)a*a%c;
        b>>=1;
    }
    return ans;
}
ll res[67][67]={0};
ll C(ll n,ll m){
    if(m==0 || m==n) return 1;
    if(res[n][m] != 0)return res[n][m];
    return res[n][m] = C(n-1,m)+ C(n-1,m-1);
}
void solve(){
    int n;cin>>n;
    vector<int>res;
    for(int j=62;j>=0;j--){
        if(n>>j&1)res.push_back(j);
    }
    ll ans=1;
    for(int i=0;i<res.size();i++){
        for(int j=0;j<=res[i];j++){
            ans=(ans*binpow(i+max(j,1ll),C(res[i],j),mod))%mod;
        }
    }
    cout<<ans<<"\n";

}

小粉兔的数位dp

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef __int128 Int;
const int mod=1e7+7;
ll binpow(ll a,ll b,ll c){
    ll ans=1;
    while(b){
        if(b&1)
        ans=(Int)ans*a%c;
        a=(Int)a*a%c;
        b>>=1;
    }
    return ans;
}
void solve(){
    int n;cin>>n;
    vector<int>dp(63);
    int idx=0;
    for(int j=62;j>=0;j--){
        for(int i=62;i>0;i--)
            dp[i]=dp[i]+dp[i-1];
        if(n>>j&1){dp[idx]++;idx++;}
    }
    dp[idx]++;
    ll ans=1;
    for(int i=1;i<63;i++){
        //cout<<i<<" "<<dp[i]<<endl;
        ans=ans*binpow(i,dp[i],mod)%mod;
    }
    cout<<ans<<"\n";
}
signed main(){
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    srand((unsigned)time(NULL));
    //int t;std::cin>>t;while(t--)
    solve();
}

唉,光理解别人的代码就费劲,那要怎么写出来呢?

标签:二进制位,res,ll,花神,int,ans,dp,数位
From: https://www.cnblogs.com/shi5/p/18042036

相关文章

  • 旅游景点 Tourist Attractions (壮压 DP)题解
    简化题意题目链接——不卡内存班题目链接——卡内存版给定\(n\)个点和\(m\)条边组成的无向图,按照一定限制要求停留\(2\simk+1\)共\(k\)个点(可以经过但不停留),求最短的从\(1\)出发到\(n\)的路径长。限制情况如下:共有\(q\)个限制,接下来\(q\)行,每行两个数\(x......
  • 状压dp
    0.位运算1.概述用01数字标志状态2.要求对象状态只能有两种,例如放/不放,正/反等等某一项指标的范围很小3.实际运用后续\(S_i\)一般表示状态(除特殊说明)特殊方格棋盘click组合:我会!\(n!\)先考虑所有格子都能放\(n\leqslant20\),可以状压\(0\)表示没放,\(1\)表示放......
  • Windows如何检测UDP端口的连通性
    在Windows平台上如何检测UDP端口的连通性呢?其实,平时我们遇到检测TCP端口的连通性的情况比较多,遇到检测UDP端口连通性的情况较少。而且检测UDP端口的连通性比较复杂一点。像检测TCP端口是否连通(放开),Windows平台,一般常用的工具有telnet、psping等工具,而检测UDP端口的工具,在Linux平台......
  • dp练习
    合并类len=2,[k]消消乐类,len=3,[i+1][j-1]else[k]Bracketshttps://vjudge.net/problem/POJ-2955#include<iostream>#include<cstring>usingnamespacestd;intdp[101][101];boolflag=1;boolpei(inti,intj,chara[]){if(a[i]=='('&&......
  • 基于STM32F407MAC与DP83848实现以太网通讯四(STM32F407MAC数据收发与DMA描述符)
    上一章实现的MAC数据包的基础收发功能,但是只是简单的操作了ETH外设的收发包函数并没有深入了解其中的原理逻辑,本章结合STM32F40x文档与STM32F4x7_ETH_Driver驱动库了解MAC的收发包流程。一、描述符列表 在创建描述符列表之前先了解描述符列表的定义,描述符就软件来说就是一个结......
  • CF932F Escape Through Leaf【DP,李超线段树】
    有一颗\(n\)个节点的树,根节点为\(1\)。每个节点有两个权值,第\(i\)个节点的权值为\(a_i,b_i\)。你可以从一个节点跳到它的子树内任意一个节点上。从节点\(x\)跳到节点\(y\)一次的花费为\(a_x\timesb_y\)。跳跃多次走过一条路径的总费用为每次跳跃的费用之和。求每个节......
  • P8935 [JRKSJ R7] 茎【DP】
    给定一棵\(n\)个点的根节点为\(1\)的有根树,现在你要对这棵树进行剪枝,每次你可以选择一个还未被剪掉的节点\(u\)进行操作,然后剪掉\(u\)的子树所有点(包括\(u\))。当且仅当你剪掉\(1\)时,操作停止。再给定\(x,k\),求有多少种不同的操作序列满足第\(k\)次恰好操作的是\(x......
  • AT_joi2015ho_b (dp思想)
    难度2比较有意思的dp题首先发现这就是将一个环从中间一点一点剥开的过程。其次观察到joi取时右端点减左端点为偶数,ioi取时为奇数,所以一次一次dp即可。看到这种题时,发现有环,就要想到双倍延长再模拟一下题意,手玩一下即可//LUOGU_RID:117752061#include<bits/stdc++.h>using......
  • ABC283E (dp思想)
    难度1这题看到一点思路也没有,但是看到最小操作数想到二分,dp和贪心,二分答案的check显然不会,贪心不会。发现对于一行,前面的\(i-2\)是不会影响的,这一行也不会影响后面的\(i+2\)行,所以是dp。考虑如何设计状态因为\(i-1\)和\(i+1\)行都会影响,所以设计出来一个dp[i][0/1][0/1][0/1]的东......
  • P10156(dp思想)
    难度2也是比较有意思的一道题。首先发现每个小团体独立,所以对于每个小团体分开直接暴力dp,dp[i][j]表示当前小团体做到第i人,走了j人,然后O(n)转移,加上部分分喜提50pts。为什么要O(n)转移呢,因为我要枚举匹配的两个人然后算贡献。但是对于这种带绝对值的贡献,我们一般都要把绝对值拆掉......