P9915 「RiOI-03」3-2

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

给定一个正整数 \(n\)。将 \([0,2^n)\) 中每个整数的二进制最低 \(n\)从低到高依次写在一个 \(2^n\times n\) 的矩阵上。矩阵两维的下标都从 \(0\) 开始。 如,当 \(n=3\) 时,矩阵是这样的:

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

Solution 数学(观察性质)

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

首先判断这个位置 \((x,y)\) 是否为 \(1~\text{or}~0\) 是简单的,(x>>y)&1 即可。

打表观察方案,发现连通块大小必然是 \(2^m-1\)简要证明:观察到每个连通块都可以拆成 \(\sum_{i=0}^{m} 2^i\),该式子的值为:\(2^{m+1}\)

同时由上面的简要证明可知,从当前位置开始往右走,假设到 \((x,r)\) 时与原位置的数不一致或者向右走出表格外(此时 \(r=n\)),那么当前位置所处的连通块的大小即为 \(2^r-1\)

一个优化:在 \(y \ge 60\) 时,直接输出 \(2^n-1\) 即可,因为此时该位置必然处于最大连通块。同时也避免了 \(2^y\) 超出 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;
}
posted @ 2024-05-29 21:52  Mr_Azz  阅读(5)  评论(0编辑  收藏  举报