2021-2022-1 ACM集训队每周程序设计竞赛(4) - 问题 A:异或&加法: - 题解

题意
给定一个二进制表示的数 n n n,问有多少对自然数 ( x , y ) (x,y) (x,y) 满足 x + y ⩽ n x+y \leqslant n x+yn x + y = = x ⊕ y x+y==x \oplus y x+y==xy

思路
这是一道数位 d p dp dp
由于异或是无进位相加,所以 x + y = = x ⊕ y x+y==x \oplus y x+y==xy 等价于 x x x y y y 转化为二进制之后,每一位都不会出现两个 ′ 1 ′ '1' 1
x i x_{i} xi 表示 x x x 的二进制表示形式的第 i i i 位。

首先考虑一下特殊情况,如果 n n n的二进制表示形式全部是都是由 ′ 1 ′ '1' 1组成的,那么可以想象到, x , y , x + y x,y,x+y x,y,x+y ⩽ n \leqslant n n,即对于 i ( 1 ⩽ i ⩽ l e n g t h ( n ) ) i(1 \leqslant i \leqslant length(n)) i(1ilength(n)),都可能出现 x i = = 0 & y i = = 0 ( { 0 , 0 } ) x_{i}==0\&y_{i}==0 (\{0,0\}) xi==0&yi==0({0,0}) x i = = 1 & y i = = 0 ( { 1 , 0 } ) x_{i}==1\&y_{i}==0 (\{1,0\}) xi==1&yi==0({1,0}) x i = = 0 & y i = = 1 ( { 0 , 1 } ) x_{i}==0\&y_{i}==1 (\{0,1\}) xi==0&yi==1({0,1}) 这三种情况,因此 ( x , y ) (x,y) (x,y) 总共有 3 l e n g t h ( n ) 3^{length(n)} 3length(n) 种。

但是如果 n n n的二进制表示形式中有 ′ 0 ′ '0' 0的话就会比较难办了,因为 3 l e n g t h ( n ) 3^{length(n)} 3length(n) 种情况中必然有一些使 x + y > n x+y>n x+y>n 等不合法的自然数对出现。 既然这样,我们只需要将 x + y < n x+y<n x+y<n x + y = n x+y=n x+y=n 的情况均求出来即可。
d [ i ] [ 0 ] d[i][0] d[i][0] 表示 ( x + y ) j = = n j ( 1 ⩽ j ⩽ i ) (x+y)_{j}==n_{j}(1\leqslant j \leqslant i) (x+y)j==nj(1ji) 的情况数, d [ i ] [ 1 ] d[i][1] d[i][1] 表示 1 ⩽ j ⩽ i 1\leqslant j \leqslant i 1ji 中存在至少一位 j j j,使得 ( x + y ) j < n j (x+y)_{j}<n_{j} (x+y)j<nj 的情况数。

那么当 n i = 1 n_{i}=1 ni=1 时,对于相等的情况,有 { 1 , 0 } 、 { 0 , 1 } \{1,0\}、\{0,1\} {1,0}{0,1} 这两种可能,因此 d [ i ] [ 0 ] = d [ i − 1 ] [ 0 ] ∗ 2 d[i][0]=d[i-1][0]*2 d[i][0]=d[i1][0]2 ;而对于小于的情况,可以是前 i − 1 i-1 i1位小于的方案数 ∗ 3 *3 3,表示 { 0 , 0 } 、 { 1 , 0 } 、 { 0 , 1 } \{0,0\}、\{1,0\}、\{0,1\} {0,0}{1,0}{0,1} 三种情况都合法,即 d [ i ] [ 1 ] = d [ i − 1 ] [ 1 ] ∗ 3 d[i][1]=d[i-1][1]*3 d[i][1]=d[i1][1]3;也可以是前 i − 1 i-1 i1位均相等的方案,再加上 { 0 , 0 } \{0,0\} {0,0} ,即在第 i i i 位时开始小于, d [ i ] [ 1 ] + = d [ i − 1 ] [ 0 ] d[i][1]+=d[i-1][0] d[i][1]+=d[i1][0]
而当 n i = 0 n_{i}=0 ni=0 时,小于的情况只可能是前 i − 1 i-1 i1位小于的方案数 ∗ 3 *3 3 d [ i ] [ 1 ] = d [ i − 1 ] [ 1 ] ∗ 3 d[i][1]=d[i-1][1]*3 d[i][1]=d[i1][1]3,而等于也只有 { 0 , 0 } \{0,0\} {0,0} 一种情况, d [ i ] [ 0 ] = d [ i − 1 ] [ 0 ] d[i][0]=d[i-1][0] d[i][0]=d[i1][0]

注意初始化时, d [ 1 ] [ 0 ] = 1 , d [ 1 ] [ 1 ] = 0 d[1][0]=1,d[1][1]=0 d[1][0]=1,d[1][1]=0
下面代码中 d [ i ] [ 0 ] d[i][0] d[i][0] 表示为 r e s res res d [ i ] [ 1 ] d[i][1] d[i][1] 表示为 c n t cnt cnt

时间复杂度 O ( n ) O(n) On

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;

int main() {
	string n;
	cin >> n;

	long long res = 1, cnt = 0;
	for (long long i = 0; i < n.size(); i++) {
		cnt = (cnt * 3) % mod;//d[i][1]=d[i-1][1]*3

		if (n[i] == '1') {
			cnt = (cnt + res) % mod;//d[i][1]+=d[i-1][0]
			res = (res * 2) % mod;//d[i][0]=d[i-1][0]*2
		}
	}

	cout << (res + cnt) % mod;//d[length(n)][0]+d[length(n)][1]
	return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值