难度: 提高+/省选 \colorbox{#3498db}{\color{White}提高+/省选} 提高+/省选
给定一个正整数 n n n。将 [ 0 , 2 n ) [0,2^n) [0,2n) 中每个整数的二进制最低 n n n 位从低到高依次写在一个 2 n × n 2^n\times n 2n×n 的矩阵上。矩阵两维的下标都从 0 0 0 开始。 如,当 n = 3 n=3 n=3 时,矩阵是这样的:
给定 q q q 次询问,每次询问这个矩阵下标为 ( x , y ) (x,y) (x,y) 的格子所在的四连通块大小对 998244353 998244353 998244353 取模的值。
应该是本题最简单,容易理解和实现的方法。
首先判断这个位置 ( x , y ) (x,y) (x,y) 是否为 1 or 0 1~\text{or}~0 1 or 0 是简单的,(x>>y)&1
即可。
打表观察方案,发现连通块大小必然是 2 m − 1 2^m-1 2m−1,简要证明:观察到每个连通块都可以拆成 ∑ i = 0 m 2 i \sum_{i=0}^{m} 2^i ∑i=0m2i,该式子的值为: 2 m + 1 2^{m+1} 2m+1。
同时由上面的简要证明可知,从当前位置开始往右走,假设到 ( x , r ) (x,r) (x,r) 时与原位置的数不一致或者向右走出表格外(此时 r = n r=n r=n),那么当前位置所处的连通块的大小即为 2 r − 1 2^r-1 2r−1。
一个优化:在 y ≥ 60 y \ge 60 y≥60 时,直接输出 2 n − 1 2^n-1 2n−1 即可,因为此时该位置必然处于最大连通块。同时也避免了 2 y 2^y 2y 超出 long long
范围的错误(准确来说,这是未定义行为)。
int n,q;
ll x,y;
inline ll two(int p){
ll res=1,a=2;
while(p){
if(p&1) res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res%mod;
}//2^n 快速幂
inline bool check(ll x,ll y){return ((x>>y)&1);}//改位置为1或0
signed main(){
read(n,q);ll num=two(n);
for(rint i=1;i<=q;i++){
read(x,y);
if(y>=60){//特判
printf("%lld\n",num-1);
continue;
}
int t=check(x,y);//当前位置的数
while(check(x,y)==t&&y<n) y++;//若不同则停止,相同则往右走
printf("%lld\n",two(y)-1);//y不需要-1,请自行思考原因
}
return 0;
}