【算法•日更•第十二期】信息奥赛一本通1585:【例 1】Amount of Degrees题解
废话不多说,直接上题:
1585: 【例 1】Amount of Degrees
时间限制: 1000 ms 内存限制: 524288 KB
提交数: 130 通过数: 68
【题目描述】
原题来自:NEERC 2000 Central Subregional,题面详见 Ural 1057。
求给定区间 [X,Y] 中满足下列条件的整数个数:这个数恰好等于 K 个互不相等的 B 的整数次幂之和。例如,设 X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
17=24+20
18=24+21
20=24+22
【输入】
第一行包含两个整数 X 和 Y,接下来两行包含整数 K 和 B。
【输出】
只包含一个整数,表示满足条件的数的个数。
【输入样例】
15 20
2
2
【输出样例】
3
【提示】
数据范围与提示:
对于全部数据,1≤X≤Y≤231−1,1≤K≤20,2≤B≤10。
【来源】
这道题一看就会先想到暴力||搜索算法,但是,它们的时间复杂度还是太高,拿不了满分。仔细一看数据规模,就会发现数据规模实在太大了,于是我们就不能设计O(n2)或更高时间复杂度的算法了,甚至O(n)都存在问题,无法满足,太血腥了,我们不得不设计一个O(log n)的算法。
但是这么快的算法绝对不可能是拿来数据直接算的,那么就要采用预处理的方法,减小时间复杂度。
在这之前,先来思考,O(log n)听着这么洋气,那么什么东西和这有关呢?对,完全二叉树的层数!没错,我们虽然不需要用到树的数据结构,但是也要在脑海中想象一棵完全二叉树。算了,不用想象了,直接上图:
先吐槽一下,一本通提高篇上的图怎么没有颜色,哪来什么蓝、绿、紫子树。
这才是正宗的图:
题目中只告诉了B进制,那么我们可以先假设是二进制,那么就可以画出一棵如上图所示的深度为4的完全二叉树。假设当前要求0~13区间内k=3,b=2满足要求的数的个数,13的二进制表示是1101。为了方便,根节点就为0表示;
没错,图中红线所穿过的节点正是13,那么0~13区间就分为了图中的蓝子树、绿子树、紫子树和13四个部分;细细观察就会发现每一棵带着其他颜色的子树都是13这条路径上节点的左子树。
很明显,所有满足条件的数的路径上都不多不少有3个“1”,其实1不仅是这些数二进制中的组成部分,更是这个位置上的次方选还是不选的标志,1是选,0是不选,如果不懂,可以亲手画图试试就知道了;
那么我们不妨用f[i][j]来表示深度为i的完全二叉树中选j个“1”的符合条件的数的个数。那么则有f[i][j]=f[i-1][j]+f[i-1][j-1],为什么呢?实在不好理解,那么就直接上个图吧:
这样就清晰多了,红线下的是第i层,红线上的就是第i-1层,那么我们会发现i-1层选j个的数都会和第i层的0相接,形成f[i-1][j]个满足条件的数;反之i-1层选j-1个数的都会和i层的1相接,形成f[i-1][j-1]个满足条件的数,那么f[i][j]自然就是它们的和了。
这就是预处理。
很明显,[x,y]区间内的满足条件的数的个数可以表示为[0,y]区间满足条件个数-[0,x-1]区间满足条件个数(类似于前缀和)。
那么我们在计算[0,y]区间满足条件个数和[0,x-1]区间满足条件个数时,只要一直在向右走时(这一位是1)把左子树满足条件个数记录上,最后再判断一下当前路径是否满足条件即可。
如果你看过刘聪的浅谈数位(戳这里了解),那么你一定会认为他讲的很详细。
但是结尾处有一句话:对于询问n,我们需要求出不超过n的最大B进制数,表示只含0、1的数:找到n的左起第一位非0、1的数位,将它变为1,并将它右面所有数位设成1。将得到的B进制数表示视为二进制进行询问即可。(出自刘聪的浅谈数位&信息奥赛一本通提高篇)
这句话只写作法,不写原因,使小编很长时间不能理解。
其实很好理解,比方说13,我们将它变成2进制的形式,那么就是(1101)2,也可以表示为这样的形式:13=1*23+1*22+0*21+1*20,那么对于一个数n,就可以这样表示:n = ai*ni + ai-1*ni-1 + ai-2*ni-2……所有的ai、ai-1、ai-2……不就都是0或1吗?(因为是二进制),注意审题,题中满足条件的数像这样表示,那么ai就只能是0或1,否则就不满足条件。回归正题:比方说593,变成8进制就是(1121)8,那么2就是第一个非1、0数位,那么就会把它变成(1111)8,这是为什么呢?我们会轻易的发现(1111)8到(1121)8区间内是绝对没有满足条件的数的,所以无需再找。其实说白了这个操作并不是转进制,而是1是能选,0是不选罢了,这个操作只是找到最大的区间内符合条件的数,刚好能当二进制罢了。
哎,我也是厉害了,竟然能对着这行代码耗了一中午。不说了,直接上代码,详见注释:
#include<iostream>
using namespace std;
int x,y,k,b,s[],f[][];
int turn(int a,int b)
{
int cnt=,ans=;
while(a)//转成b进制
{
s[++cnt]=a%b;
a/=b;
}
for(int i=cnt;i>=;i--)
{
if(s[i]>)//找到第一个不是0、1的数位
{
for(int j=i;j>=;j--)
s[j]=;//全部改成1,这是最大的比a小的符合要求的数
break;
}
}
for(int i=;i<=cnt;i++) if(s[i]) ans=ans|(<<(i-));//这是位运算,在二进制中相当于十进制的加法
return ans;
}
void init()//预处理
{
f[][]=;//这个千万别忘了
for(int i=;i<=;i++)
{
f[i][]=;//因为选0个“1”所以只有0,0,0,0,0,0……这条路径可以
for(int j=;j<=i;j++) f[i][j]=f[i-][j]+f[i-][j-];//详见上文
}
}
int calc(int num,int j)
{
int cnt=,ans=;
for(int i=;i>;i--)
{
if((<<i)&num)//(位运算)如果当前这一位是1
{
cnt++;
if(cnt>j) break;//已经多了,就不必找了
num=num^(<<i);//将这一位变成0,对后面有用
}
if((<<(i-))<=num)//如果下一位是1
ans+=f[i-][j-cnt];//加上相应的左子树的值
}
if(cnt+num==j) ans++;//如果刚好够,那么当前路径也要计入
return ans;
}
int main()
{
cin>>x>>y>>k>>b;
x=turn(x,b);y=turn(y,b);
init();
cout<<calc(y,k)-calc(x-,k);//前缀和
return ;
}
【算法•日更•第十二期】信息奥赛一本通1585:【例 1】Amount of Degrees题解的更多相关文章
- 【算法•日更•第十期】树型动态规划&区间动态规划:加分二叉树题解
废话不多说,直接上题: 1580:加分二叉树 时间限制: 1000 ms 内存限制: 524288 KB提交数: 121 通过数: 91 [题目描述] 原题来自:NOIP 20 ...
- 【算法•日更•第十九期】动态规划:RMQ问题
▎前言 首先先来说一下RMB是什么,当然是人民币啦. 今天我们要学的这个东西不一般,叫做RMQ问题,那么它和RMB有什么关系呢?待小编细细说来. ▎前置技能:动态规划 不会的同志请戳这里迅速了解动态规 ...
- 【算法•日更•第二十八期】图论:强连通+Tarjan算法(一)
▎前言 一直都想学习这个东西,以为很难,结果发现也不过如此. 只要会些图论的基础就可以了. ▎强连通 ☞『定义』 既然叫强连通,那么一定具有很强的连通性. 强连通:就是指在一个有向图中,两个顶点可以互 ...
- 【算法•日更•第二十三期】数据结构:two-pointer(尺取法)&莫队
▎引入 ☞『例题』 一道十分easy的题: 洛谷P1638 长度为n的序列,m种数 找一个最短区间,使得所有数出现一遍 n≤1e6 ,m≤2e3. ☞『分析』 这道题非常的简单,但是如果不会two-p ...
- 【算法•日更•第六期】头脑风暴:洛谷P1528 切蛋糕题解
▎(一个没有用处的)前言 为什么这次题解特意写明题号呢?因为我发现了这样的事情: 所以不要混了,想看P1714题解的同志们可以圆润的滚开了. 好了,不说没用的了,切入正题: ▎题目 题目及测评链接:戳 ...
- 【算法•日更•第四十二期】离散傅里叶变换(DFT)
▎前言 小编相当的菜,这篇博客难度稍高,所以有些可能不会带有证明,博客中更多的是定义. 我们将要学到的东西: 复数 暴力多项式乘法 DFT 当然,小编之前就已经写过一篇博客了,主要讲的就是基础多项式, ...
- 【算法•日更•第三十二期】教你用出windows体验的Linux
▎前言 小编昨天闲的不行,就装了一个linux系统,linux的发行版很多,小编认为ubuntu很好用,于是就在使用ubuntu. 没错,我现在就在使用ubuntu来写博客. 刚才还装了一个QQ,不过 ...
- 【算法•日更•第三十一期】KMP算法
▎前言 这次要讲的HMP算法KMP算法很简单,是用于处理字符串的,之前一直以为很难,其实也不过如此(说白了就是优化一下暴力). ▎处理的问题 通常处理的问题是这样的:给定两个字符串s1和s2,其中s1 ...
- 【算法•日更•第五十四期】知识扫盲:什么是operator?
▎前言 这个东西和迭代器长的很像,但是比迭代器常见的多. 今天就来浅谈operator. ▎定义 operator是C#.C++和pascal的关键字,它和运算符一起使用,表示一个运算符函数,理解时应 ...
随机推荐
- node学习第一天
创建服务器 利用require引入http模块:var http=require("http") 利用http模块创建server服务器; 创建服务器:var server=htt ...
- @Autowired还可以注入List和Map
@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collection ...
- web自动化 -- Select(下拉选择框操作)
目标:(现在 select 这种已经很少了.一般都是 ul/li 或者 span/svg) 代码示例:
- spring学习(六)注解方式实现AOP
一.导包(导入maven的依赖) <?xml version="1.0" encoding="UTF-8"?> <project xmlns= ...
- random模块(验证码小程序)
#!/usr/bin/env python #-*- coding:utf-8 -*- import random li=[] for i in range(6): #循环几次,就代表生成几位的验证码 ...
- STL入门--sort,lower_bound,upper_bound,binary_search及常见错误
首先,先定义数组 int a[10]; 这是今天的主角. 这四个函数都是在数组上操作的 注意要包含头文件 #include<algorithm> sort: sort(a,a+10) 对十 ...
- Python while 循环中使用 else 语句
Python while 循环中使用 else 语句: else:表示 while 中的语句正常执行完,然后执行 else 语句的部分. 示例: # while 判断条件: # 一行语句 或 多行语句 ...
- 如何使用PHP验证客户端提交的表单数据
PHP 表单验证 本章节我们将介绍如何使用PHP验证客户端提交的表单数据. PHP 表单验证 在处理PHP表单时我们需要考虑安全性. 本章节我们将展示PHP表单数据安全处理,为了防止黑客及垃圾信息我们 ...
- PHP date_diff() 函数
------------恢复内容开始------------ 实例 计算两个日期间的差值: <?php$date1=date_create("2013-03-15");$da ...
- PHP fputs() 函数
定义和用法 fputs() 函数将内容写入一个打开的文件中. 函数会在到达指定长度或读到文件末尾(EOF)时(以先到者为准),停止运行. 如果函数成功执行,则返回写入的字节数.如果失败,则返回 FAL ...