2024杭电ACM-个人PK赛(1)
1004 合法数对
Time Limit (Java / Others)
2000 / 1000 MS
Memory Limit (Java / Others)
327680 / 327680 K
Ratio (Accepted / Submitted)
12.06% (219/1816)
Problem Description
数对 ( x , y ) (x,y) (x,y) 是好的,当且仅当 x ⊕ y = x ∣ y x⊕y=x∣y x⊕y=x∣y,其中 ⊕ ⊕ ⊕ 表示异或操作, ∣ ∣ ∣ 表示或运算。
给定正整数 N N N,请你求解有多少个好的数对 ( x , y ) (x,y) (x,y),满足 x , y ∈ [ 0 , N ] x,y∈[0,N] x,y∈[0,N]。
因为出题人小 M M M 比较仁慈,所以 N N N 将以二进制的形式给出。
答案对 998244353 998244353 998244353 取模。
Input
输入共 1 1 1 行,包含一个正整数 N N N,通过二进制的形式给出。
评测数据规模:
对于所有测评数据, 1 ≤ N < 2 1234567 1≤N<2^{1234567} 1≤N<21234567。
Output
输出共 1 1 1 行,输出 1 1 1 个整数,表示最终答案,答案对 998244353 998244353 998244353 取模。
Sample Input
111 111 111
Sample Output
27 27 27
思路分析
认识“异或”和“或”
- 异或 (
⊕
⊕
⊕ )
0 ⊕ 0 = 0 0⊕0=0 0⊕0=0
0 ⊕ 1 = 1 0⊕1=1 0⊕1=1
1 ⊕ 0 = 1 1⊕0=1 1⊕0=1
1 ⊕ 1 = 0 1⊕1=0 1⊕1=0 - 或 (
∣
|
∣ )
0 ∣ 0 = 0 0|0=0 0∣0=0
0 ∣ 1 = 1 0|1=1 0∣1=1
1 ∣ 0 = 1 1|0=1 1∣0=1
1 ∣ 1 = 1 1|1=1 1∣1=1
分析与计算
易知当 ( x , y ) = ( 1 , 1 ) (x,y)=(1,1) (x,y)=(1,1) 时不满足题意,由此可以延伸至 N N N 的每一位。
通过计算得知,输入每增加
1
1
1,输出的变化量
D
D
D 与
0
0
0 的个数
m
m
m 有关。
① 只有在与
0
0
0 进行异或
以及或
运算的时候才会相等。
② 进行运算的位可以取值
0
0
0 或
1
1
1 两种,所以
d
=
2
m
d=2^m
d=2m。
③
(
x
,
y
)
(x,y)
(x,y) 与
(
y
,
x
)
(y,x)
(y,x) 视为不同的数对,所以
D
=
d
×
2
=
2
m
+
1
D=d\times2=2^{m+1}
D=d×2=2m+1。
然后没什么思路了,打个表先,看看能不能找到一点规律。
Input | N.count(0) | D | Output | Funct |
---|---|---|---|---|
0 0 0 | 1 1 1 | 1 1 1 | 1 1 1 | |
1 1 1 | 0 0 0 | 2 2 2 | 3 3 3 | 1 × 3 1\times3 1×3 |
10 10 10 | 1 1 1 | 4 4 4 | 7 7 7 | |
11 11 11 | 0 0 0 | 2 2 2 | 9 9 9 | 3 × 3 3\times3 3×3 |
100 100 100 | 2 2 2 | 8 8 8 | 17 17 17 | |
101 101 101 | 1 1 1 | 4 4 4 | 21 21 21 | 7 × 3 7\times3 7×3 |
110 110 110 | 1 1 1 | 4 4 4 | 25 25 25 | |
111 111 111 | 0 0 0 | 2 2 2 | 27 27 27 | 9 × 3 9\times3 9×3 |
1000 1000 1000 | 3 3 3 | 16 16 16 | 43 43 43 | |
1001 1001 1001 | 2 2 2 | 8 8 8 | 51 51 51 | 17 × 3 17\times3 17×3 |
1010 1010 1010 | 2 2 2 | 8 8 8 | 59 59 59 | |
1011 1011 1011 | 1 1 1 | 4 4 4 | 63 63 63 | 21 × 3 21\times3 21×3 |
1100 1100 1100 | 2 2 2 | 8 8 8 | 71 71 71 | |
1101 1101 1101 | 1 1 1 | 4 4 4 | 75 75 75 | 25 × 3 25\times3 25×3 |
1110 1110 1110 | 1 1 1 | 4 4 4 | 79 79 79 | |
1111 1111 1111 | 0 0 0 | 2 2 2 | 81 81 81 | 27 × 3 27\times3 27×3 |
… | … | … | … | … |
x x x 0 xxx0 xxx0 | m m m | 2 m + 1 2^{m+1} 2m+1 | f ( x x x ) × 3 − 2 m f(xxx)\times3-2^m f(xxx)×3−2m | |
x x x 1 xxx1 xxx1 | m − 1 m-1 m−1 | 2 m 2^m 2m | f ( x x x ) × 3 f(xxx)\times3 f(xxx)×3 | f ( x x x ) × 3 f(xxx)\times3 f(xxx)×3 |
不难看出,当末位为
1
1
1 时,可由
N
N
N 右移一位的结果乘
3
3
3 得到答案。
当末位为
0
0
0 时,可由
N
N
N 右移一位的结果乘
3
3
3 再减去
d
=
2
m
d=2^m
d=2m 得到答案。
这里采用减法是因为从
x
x
x
1
xxx1
xxx1 到
x
x
x
0
xxx0
xxx0 不涉及进位退位,便于计算。
便可以从末位向前遍历,得到的这个计算过程将是倒序的。
为了避免递归的写法爆栈,手动把计算过程压入堆栈(后进先出)达到反序的目的。
本代码使用了 STL
中的 vector
来模拟堆栈。
代码实现
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll q_pow(ll a,ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans % mod; }
vector<ll> v;
int main(){
ll b = 0; string ss; cin >> ss;
for(auto p : ss) if(p == '0') b++; //记录0的个数
while(!ss.empty()){
if(*ss.rbegin()=='1') v.push_back(0); //不需要减,用0占位
else v.push_back(b--); //存入即时0的个数
ss.pop_back();
}ll ans=1;
while(!v.empty()){
ans *= 3;
if(*v.rbegin()) ans -= q_pow(2, *v.rbegin());
ans = (ans+mod)%mod; //保证ans>0
v.pop_back();
}cout << ans << "\n";
}
。。。我写复杂了,去看这篇吧,写得挺好的【2024杭电ACM-个人PK赛(1) 1004合法数对 - HDU7415】