题目链接

HDU7415 - PKSAI1004

题意

给定NN(以二进制的形式给出),求满足x,y[0,N]x,y \in [0,N],且xy=xyx \oplus y = x \,|\, y 的数对(x,y)(x,y) 的个数(对998244353998244353 取模).


思路

(不想看分析的可直接看 结论代码

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

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

如果N=111nN=\underbrace{11\dots1}_{n个},那么对于每一位,(xi,yi)(x_i,y_i) 都有33 种取值,答案为3n3^n.(样例中2727 的来源)

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


例子

先举个例子:N=100N=100:从高位开始,如果(x0,y0)=(1,0)(x_0,y_0)=(1,0),那么对于后面两位,x1,x2x_1,x_2 只能取00,而y1,y2y_1,y_2 则无限制.

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

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

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

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


一般情况

因为是一位一位地看的,设前ii 位是N0iN_{0\sim i},其答案为ans\text{ans},考虑下一位(第i+1i+1 位).

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

如果下一位是00,减少的情形应该只有当x0i=N0ix_{0\sim i}=N_{0\sim i}(或y0i=N0iy_{0\sim i}=N_{0\sim i},先讨论xx),因为此时下一位被NN 限制住了只能取00,情形会减少;否则如果xx 的前ii 位有一位与NN 不同,下一位都是可以任意取的.

考虑x0i=N0ix_{0\sim i}=N_{0\sim i},且xi+1=1x_{i+1}=1yy 的可能取值,这是不合题意的,所以这时yy 的可能取值数量就是不合题意的数量.

如果x0ix_{0\sim i} 的某一位xjx_j11 那么yjy_j 只能取00,有一种取值,如果是00 那么yjy_j 可以取0011,有两种取值,如果前ii 位有mm00,依据乘法原理就有2m2^m 种取值,产生了2m2^m 种不合题意的情形,应当减去2m2^m
y0i=N0iy_{0\sim i}=N_{0\sim i} 是同理的,所以说,结果应当减去2×2m2\times2^m,是3ans2m+13\text{ans}-2^{m+1}


结论

ans(N0i)\operatorname{ans}(N_{0\sim i}) 表示将NN 的前ii 位作为NN 对应的数对个数,mim_i 表示NN 的前ii 位中00 的个数,有

ans(N0i)={3,i=03ans(N0i1),i>0Ni=13ans(N0i1)2mi,i>0Ni=0\operatorname{ans}(N_{0\sim i})= \begin{cases} 3, & i=0 \\ 3\operatorname{ans}(N_{0\sim i-1}), & i>0 \land N_i=1 \\ 3\operatorname{ans}(N_{0\sim i-1})-2^{m_i}, & i>0 \land N_i=0 \\ \end{cases}

实现不难,这里就不详细解释了,直接放代码.


ACcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#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),实测109ms\text{109ms},最优化可达到15ms\text{15ms}