P6218 [USACO06NOV] Round Numbers S 题解
题面
如果一个正整数的二进制表示中,
例如,
请你计算,区间
前置芝士
1.数位dp
相关的题:P4317 花神的数论题
思路
l,r的数据范围为
数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:
1.要求统计满足一定条件的数的数量(即,最终目的为计数);
2.这些条件经过转化后可以使用「数位」的思想去理解和判断;
3.输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
4.上界很大(比如),暴力枚举验证会超时。
看到上面从oiwiki里抄的描述,发现完美符合本题,于是我们考虑使用数位dp来解决这道题
本题解使用的是记忆化搜索的方式(其实是不会递推写法喵)
状态设计
看到洛谷题解区的大佬好像很多都是用一维表示0和1的个数的差,也没有在
这个表示:枚举到第
前导0
关于为什么要看前面的每一位数是不是都是前导0:
因为如果不判断前导0的话,类似于
tips:
1.因为这个题的数据范围只有
2.在2进制中
3.主包写代码写一半忘记是2进制了,怒调两分半。
4.记得初始化
代码:
#include<bits/stdc++.h>
using namespace std;
int l,r;
const int MAXN=35;
int a[MAXN];//十进制的数拆分成2进制每一位是什么
int dp[MAXN][MAXN][MAXN][2];//枚举到第i位,有j个0,k个1,前面是不是都是前导0的数的个数
//dfs返回的是这种情况下合法的数的个数
int dfs(int cnt,int flag,int sum0,int sum1,int qd){
//cnt:枚举到了cnt位,flag:这一位有没有限制,其他的看上文:P
if(qd){//如果前面全都是前导0,那么这些0都不能算进0的总个数中
sum0=0;
}
//下面的就是数位dp经典模板了(划掉
if(!cnt){//已经枚举完这个数啦
if(sum0>=sum1){//合法的数
return 1;
}
return 0;//不合法的数
}
if(!flag && ~dp[cnt][sum0][sum1][qd]){//记忆化,只记录过没有限制的,因为有限制的只会搜一次
return dp[cnt][sum0][sum1][qd];
}
int maxi=a[cnt];
if(!flag){
maxi=1;
}//maxi是指这一位最大可以枚举到多少,如果有限制的话就是原数二进制的cnt位的数字
int ret=0;
for(int i=0;i<=maxi;i++){
ret+=dfs(cnt-1,flag&&(i==maxi),sum0+(i==0),sum1+(i==1),qd&&(i==0));
}//枚举下一位数字
if(!flag){
return dp[cnt][sum0][sum1][qd]=ret;//记忆化,只记录没有限制的,因为有限制的只会搜一次
}//这也是dp数组可以不存flag的原因
return ret;
}
int query(int x){
int cnt=0;
while(x){
cnt++;
a[cnt]=x&1;
x>>=1;
}//拆分成二进制放在a数组里
return dfs(cnt,1,0,0,1);//从最后一位开始,有限制,0和1的个数都是0,有前导零
}
int main(){
cin>>l>>r;
memset(dp,-1,sizeof(dp));//记得初始化
cout<<query(r)-query(l-1);
}
结语
呃呃,真的写不动题了啊啊,只能来水题解了。(吐血
补了一些latex,好像更丑了......
我的洛谷(魂兮归来......
作者:Le-Louvre
出处:https://www.cnblogs.com/Le-Louvre/p/18367710
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现