Getting Zero(思维 + lowbit)

Getting Zero(思维 + lowbit)

题目链接

题意:

​ 给一个数 n n n ,两种操作: n + 1 n+1 n+1 n ∗ 2 n*2 n2 ,求使 n   %   327681 = 0 n \ \% \ 327681 =0 n % 327681=0 的最小操作数

思路:

327681 = 2 15 327681 = 2 ^ {15} 327681=215, 那么对于 n   %   2 15 = 0 n \ \% \ 2 ^ {15} = 0 n % 215=0 我们就可以这样思考:因为是对 2 15 2 ^ {15} 215 这样一个 2 2 2 的幂次的数,那么我们就可以考虑这样一个操作在二进制表示上是否有特殊性。

​ 显然,在二进制上对一个数 n   %   2 15 n \ \% \ 2 ^ {15} n % 215 这样一个操作,可以看做只保留 n n n 在二进制上的前 15 15 15 位,即 2 0 − 2 14 2 ^ {0} - 2 ^ {14} 20214 位上的数。

​ 那么在考虑两种操作在二进制上的意义:

  • n + 1 n + 1 n+1 就是在最后一位上加 1 1 1 ,但是会存在进位的后续问题
  • n ∗ 2 n * 2 n2 就是将原 n n n 二进制上的所有数向前移动一位,比如 n = 1011 n = 1011 n=1011 ,那么 n ∗ 2 = 10110 n * 2 = 10110 n2=10110

​ 那我们最终要使 n   %   2 15 = 0 n \ \% \ 2 ^ {15} = 0 n % 215=0,其实就是使 n n n 的前 15 15 15 位都为 0 0 0,明显最简单的方法就是不断重复 n ∗ 2 n * 2 n2,这样只要将 n n n 的最低位的 1 1 1 移动到 15 15 15 位之后,就可以了。

​ 那么考虑最坏的情况: n   %   2 = 1 n \ \% \ 2 = 1 n % 2=1,即最低位在第一位,那么就需要移动 15 15 15 次,即操作 15 15 15 次。

​ 接下来考虑 n + 1 n + 1 n+1 操作,该操作的价值在于将 n n n 的最低位通过进位的方式提升。比如 $ n = 111$,通过 n + 1 = 1000 n + 1 = 1000 n+1=1000,最低位的 1 1 1 从第一位提升到了第四位,那么原来需要操作 15 15 15 次就减少到了 15 − 4 + 1 = 12 15 - 4 + 1 = 12 154+1=12 次,成功压缩了操作次数,使答案更优。

​ 而且考虑到 n + 1 n + 1 n+1 这一操作肯定是先操作的价值更高的。还是上面的例子: n = 111 n = 111 n=111,若是先进行 n ∗ 2 = 1110 n * 2 = 1110 n2=1110 操作,那么在进行 n + 1 n + 1 n+1 操作就需要更多次来使最低位的 1 1 1 提升。 n + 1 = 1111 , n + 1 = 10000 n + 1 = 1111, n + 1 = 10000 n+1=1111,n+1=10000

​ 所以在最优解中 n + 1 n + 1 n+1 一定是开始就进行操作了的。那么我们就可以暴力遍历:考虑到 a n s ≤ 15 ans \leq 15 ans15 (因为只进行 n ∗ 2 n * 2 n2 最差也是 15 15 15),所以 n + 1 n + 1 n+1 的操作最多也是进行 15 15 15 ,多了就一定不是最优解。

​ 那么我们就可以遍历操作,假设 n + i ( i ≤ 15 ) n + i(i \leq 15) n+i(i15),再计算对 n + i n + i n+i 需要进行多少 ( j ) (j) (j) n ∗ 2 n * 2 n2 操作才能满足题目要求,然后 a n s i j = i + j ans_{ij} = i + j ansij=i+j,取最小的 a n s i j ans _ {ij} ansij即可。

​ 最后补充对 j j j 的求解,设 l o w low low n n n 最低位的 1 1 1 的下标,那么 j = 15 − l o w j = 15 - low j=15low,而 l o w low low 可以采用 l o w b i t lowbit lowbit 算法: l o w = n   & − n low = n \ \& -n low=n &n,其中 − n -n n n n n 在二进制上取负数,就等于 n n n 的补集加 1 1 1 ,如 n = 10010 n = 10010 n=10010, 那么 n n n 的补集就为 01101 01101 01101,则 − n = 01101 + 1 = 01110 -n = 01101 + 1 = 01110 n=01101+1=01110,则 & \& & 一下就是第二位的 1 1 1 n   & − n = 00010 = 2 1 n \ \& -n = 00010 = 2 ^ {1} n &n=00010=21

#include<bits/stdc++.h>
#define lazy ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);
#define inf 0x3f3f3f3f3f3f3f
#define endl '\n'
using namespace std;

typedef long long ll;
const double eps = 1e-10;
const ll N = 2e5 + 7;
const ll M = 1e9 + 7;
const ll mod = 998244353;
ll t, a[20];


int main() {
	lazy;
	a[0] = 1;
	for (int i = 1; i < 20; i++) {// 预处理出2的幂次
		a[i] = a[i - 1] * 2;
	}
	cin >> t;
	while (t--) {
		ll n;
		cin >> n;
		n %= a[15];// 放置n本就比327681大
		if (n == 0) cout << 0 << " ";// 特判0
		else {
			ll mn = 15;
			n--;
			for (int i = 0; i <= 15; i++) {// 遍历 n + i
				n++;
				ll low = n & -n;// 取最小位
				for (int i = 0; i <= 16; i++) {// 将最小位转化为位数,比如将327681转化为2的15次,把幂次数保留
					if (low == a[i]) {
						low = i;
						break;
					}
				}
				mn = min(mn, i + 15 - low);// 对答案取小
			}
			cout << mn << " ";
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值