P9915 「RiOI-03」3-2

Question 问题 P9915 「RiOI-03」3-2

难度: 提高+/省选 \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 时,矩阵是这样的:

P9915 「RiOI-03」3-2_第1张图片

给定 q q q 次询问,每次询问这个矩阵下标为 ( x , y ) (x,y) (x,y) 的格子所在的四连通块大小对 998244353 998244353 998244353 取模的值。

Solution 数学(观察性质)

应该是本题最简单,容易理解和实现的方法。

首先判断这个位置 ( 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 2m1简要证明:观察到每个连通块都可以拆成 ∑ 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 2r1

一个优化:在 y ≥ 60 y \ge 60 y60 时,直接输出 2 n − 1 2^n-1 2n1 即可,因为此时该位置必然处于最大连通块。同时也避免了 2 y 2^y 2y 超出 long long 范围的错误(准确来说,这是未定义行为)。

Code 代码

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;
}

你可能感兴趣的:(算法)