2024杭电ACM-个人PK赛(1) 1004合法数对 - HDU7415

HDU7415

题意

给定 N N N(以二进制的形式给出),求满足 x , y ∈ [ 0 , N ] x,y \in [0,N] x,y[0,N],且 x ⊕ y = x   ∣   y x \oplus y = x \,|\, y xy=xy 的数对 ( x , y ) (x,y) (x,y) 的个数(对 998244353 998244353 998244353 取模)。


思路

(下文中 N , x , y N,x,y N,x,y 等,若不加说明,指的是其二进制。)

比较按位异或和按位与的运算法则,不难发现,对于 x \pmb x x y \pmb y y 的(二进制的)第 i \pmb i i x i , y i \pmb{x_i,y_i} xi,yi,有 ( x i , y i ) = ( 0 , 0 )  or  ( 0 , 1 )  or  ( 1 , 0 ) \pmb{(x_i,y_i) = (0,0) \text{ or } (0,1) \text{ or } (1,0)} (xi,yi)=(0,0) or (0,1) or (1,0),不能为 ( 1 , 1 ) \pmb{(1,1)} (1,1)

如果 N = 11 … 1 ⏟ n 个 N=\underbrace{11\dots1}_{n个} N=n 111,那么对于每一位, ( x i , y i ) (x_i,y_i) (xi,yi) 都有 3 3 3 种取值,答案为 3 n 3^n 3n。(样例中 27 27 27 的来源)

如果有 0 0 0,那么数对个数就没有这么多了,因为需要保证 x , y ⩽ N x,y \leqslant N x,yN


例子

先举个例子: N = 100 N=100 N=100:从高位开始,如果 ( x 0 , y 0 ) = ( 1 , 0 ) (x_0,y_0)=(1,0) (x0,y0)=(1,0),那么对于后面两位, x 1 , x 2 x_1,x_2 x1,x2 只能取 0 0 0,而 y 1 , y 2 y_1,y_2 y1,y2 则无限制。

为什么从高位开始?简单来说,比大小是从高位比的,如果 x x x 最高位比 N N N 小,那么 x x x 一定比 N N N 小,前一位将确定性地影响后面的选择。进一步联想到,比大小中这一位相同看下一位,应该 从高位开始逐位看

回到 N = 100 N=100 N=100,我们一位一位地看:

  • 第一位是 N 0 = 1 N_0=1 N0=1:有 ans ⁡ ( N 0 ) = 3 1 = 3 \operatorname{ans}(N_0)=3^1=3 ans(N0)=31=3 种取值;
  • 再加上下一位, N 1 = 10 N_1=10 N1=10:第二位并不是总有 3 3 3 种取值,应该比 3 N 0 3N_0 3N0 少一些情形,具体地说,当 ( x 0 , y 0 ) = ( 1 , 0 ) (x_0,y_0)=(1,0) (x0,y0)=(1,0) 时,第二位不能取 ( 1 , 0 ) (1,0) (1,0),当 ( x 0 , y 0 ) = ( 0 , 1 ) (x_0,y_0)=(0,1) (x0,y0)=(0,1) 时,第二位不能取 ( 0 , 1 ) (0,1) (0,1),少了 2 2 2 种,因此 ans ⁡ ( N 1 ) = 3 × 3 − 3 = 7 \operatorname{ans}(N_1)=3\times3-3=7 ans(N1)=3×33=7 种取值;
  • 接着向下看, N 2 = 100 N_2=100 N2=100:它应该比 3 N 1 3N_1 3N1 少一些情形,具体地说,当 ( x 01 , y 01 ) = ( 10 , 00 ) (x_{01},y_{01})=(10,00) (x01,y01)=(10,00) 时,第三位不能取 ( 1 , 0 ) (1,0) (1,0),当 ( x 01 , y 01 ) = ( 10 , 01 ) (x_{01},y_{01})=(10,01) (x01,y01)=(10,01) 时,第三位亦不能取 ( 1 , 0 ) (1,0) (1,0),少了 2 2 2 种,当 ( x 01 , y 01 ) = ( 0 ? , 10 ) (x_{01},y_{01})=(0?,10) (x01,y01)=(0?,10) 时,也少了 2 2 2 种,共少了 4 4 4 种,因此 ans ⁡ ( N 2 ) = 3 × 7 − 4 = 17 \operatorname{ans}(N_2)=3\times7-4=17 ans(N2)=3×74=17 种取值。

读者可以再写一些数感受一下,这里直接讨论一般情况了:


一般情况

因为是一位一位地看的,设前 i i i 位是 N 0 ∼ i N_{0\sim i} N0i,其答案为 ans \text{ans} ans,考虑下一位(第 i + 1 i+1 i+1 位)。

如果下一位是 1 1 1,显然 3 3 3 种取值都能取到,结果是 3 ans 3\text{ans} 3ans

如果下一位是 0 0 0,减少的情形应该只有当 x 0 ∼ i = N 0 ∼ i x_{0\sim i}=N_{0\sim i} x0i=N0i(或 y 0 ∼ i = N 0 ∼ i y_{0\sim i}=N_{0\sim i} y0i=N0i,先讨论 x x x),因为此时下一位被 N N N 限制住了只能取 0 0 0,情形会减少;否则如果 x x x 的前 i i i 位有一位与 N N N 不同,下一位都是可以任意取的。
考虑 x 0 ∼ i = N 0 ∼ i x_{0\sim i}=N_{0\sim i} x0i=N0i,且 x i + 1 = 1 x_{i+1}=1 xi+1=1 y y y 的可能取值,这是不合题意的,所以这时 y y y 的可能取值数量就是不合题意的数量。
如果 x 0 ∼ i x_{0\sim i} x0i 的某一位 x j x_j xj 1 1 1 那么 y j y_j yj 只能取 0 0 0,有一种取值,如果是 0 0 0 那么 y j y_j yj 可以取 0 0 0 1 1 1,有两种取值,如果前 i i i 位有 m m m 0 0 0,依据乘法原理就有 2 m 2^m 2m 种取值,产生了 2 m 2^m 2m 种不合题意的情形,应当减去 2 m 2^m 2m
y 0 ∼ i = N 0 ∼ i y_{0\sim i}=N_{0\sim i} y0i=N0i 是同理的,所以说,结果应当减去 2 × 2 m 2\times2^m 2×2m,是 3 ans − 2 m + 1 3\text{ans}-2^{m+1} 3ans2m+1


结论

ans ⁡ ( N 0 ∼ i ) \operatorname{ans}(N_{0\sim i}) ans(N0i) 表示将 N N N 的前 i i i 位作为 N N N 对应的数对个数, m i m_i mi 表示 N N N 的前 i i i 位中 0 0 0 的个数,有
ans ⁡ ( N 0 ∼ i ) = { 3 , i = 0 3 ans ⁡ ( N 0 ∼ i − 1 ) , i > 0 , N i = 1 3 ans ⁡ ( N 0 ∼ i − 1 ) − 2 m i , i > 0 , N i = 0 \operatorname{ans}(N_{0\sim i})=\begin{cases}3, & i=0 \\3\operatorname{ans}(N_{0\sim i-1}), & i>0, N_i=1 \\3\operatorname{ans}(N_{0\sim i-1})-2^{m_i}, & i>0, N_i=0 \\\end{cases} ans(N0i)= 3,3ans(N0i1),3ans(N0i1)2mi,i=0i>0,Ni=1i>0,Ni=0
实现不难,这里就不详细解释了,直接放代码。


AC code

#include <cstdio>
#include <cstring>
#define Code int main() { int T = 1;
#define Codes int main() { int T=read();
#define by while (T--) eachT();
#define HTLDL return 0;
#define HappyHDUPKSAI }
inline long long read() { char st; long long x = 0; int fu = 1; st = getchar(); while (st > 57 || st < 48) { if (st == 45) fu = -1; st = getchar(); }while (st <= 57 && st >= 48) { x = (x << 3) + (x << 1) + st - 48; st = getchar(); }return x * fu; }
typedef long long ll;
const ll MOD = 998244353;
char s[1234567];

void eachT() {
	scanf("%s", s);
	int len = strlen(s);
	ll ans = 1, pow = 1;
	for (int i = 0; i < len; ++i) {
		ans *= 3;
		if (s[i] == '0') {
			pow = (pow << 1) % MOD;
			ans -= pow;
		}
		ans = (ans + MOD) % MOD;
	}
	printf("%lld", ans);
}

Code by HTLDL
HappyHDUPKSAI

时间复杂度 Θ ( n ) \Theta(n) Θ(n),实测 109ms \text{109ms} 109ms,最优化可达到 15ms \text{15ms} 15ms

  • 35
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值