【CodeForces - 1080D】Olya and magical square

@Olya and magical square@

    • @Description@
    • @Translation@
    • @Solution@
    • @Code@
    • @End@


@Description@

Recently, Olya received a magical square with the size of 2n×2n.

It seems to her sister that one square is boring. Therefore, she asked Olya to perform exactly k splitting operations.

A Splitting operation is an operation during which Olya takes a square with side a and cuts it into 4 equal squares with side a2. If the side of the square is equal to 1, then it is impossible to apply a splitting operation to it (see examples for better understanding).

Olya is happy to fulfill her sister’s request, but she also wants the condition of Olya’s happiness to be satisfied after all operations.

The condition of Olya’s happiness will be satisfied if the following statement is fulfilled:

Let the length of the side of the lower left square be equal to a, then the length of the side of the right upper square should also be equal to a. There should also be a path between them that consists only of squares with the side of length a. All consecutive squares on a path should have a common side.

Obviously, as long as we have one square, these conditions are met. So Olya is ready to fulfill her sister’s request only under the condition that she is satisfied too. Tell her: is it possible to perform exactly k splitting operations in a certain order so that the condition of Olya’s happiness is satisfied? If it is possible, tell also the size of the side of squares of which the path from the lower left square to the upper right one will consist.

Input
The first line contains one integer t (1≤t≤10^3) — the number of tests.

Each of the following t lines contains two integers ni and ki (1≤ni≤109,1≤ki≤1018) — the description of the i-th test, which means that initially Olya’s square has size of 2ni×2ni and Olya’s sister asks her to do exactly ki splitting operations.

Output
Print t lines, where in the i-th line you should output “YES” if it is possible to perform ki splitting operations in the i-th test in such a way that the condition of Olya’s happiness is satisfied or print “NO” otherwise. If you printed “YES”, then also print the log2 of the length of the side of the squares through space, along which you can build a path from the lower left square to the upper right one.

You can output each letter in any case (lower or upper).

If there are multiple answers, print any.

Example
input
3
1 1
2 2
2 12
output
YES 0
YES 1
NO

@Translation@

给你 2n*2n 的正方形,让你执行恰好 k 次操作,每一次操作将一个大小不为 1 的正方形剪成四个一模一样的正方形,但是正方形不能被移动。
问:k 次操作后,能否从左下角找一条路径到达右上角(只能横着或竖着走),使得经过的正方形大小相同(包含起点与终点)。
如果可以,输出经过的正方形的边长的以 2 为底的对数。

@Solution@

基本想法是比较简单的:枚举路径上正方形的边长,判断是否合法。

怎么判断呢?我们可以算出使得合法路径存在的最小操作数与最大操作数,如果在这个范围内就合法。

从大到小依次考虑几种情况,尝试找找规律。

情况(1):
【CodeForces - 1080D】Olya and magical square_第1张图片
可以发现这时最小操作数必然为 1,不可能更少。
最大操作数呢?除了右下角那个正方形外,其他是不能被操作的。所以我们的最大操作数 = 将右下角那个正方形全部剪成 1*1 的操作数 + 1。

情况(2):
【CodeForces - 1080D】Olya and magical square_第2张图片
这个时候,我们发现操作次数 <= 3 时无论如何都不能合法,所以最小操作次数为 4。
最大操作数,一样地,不能操作那些被路径经过的数。可以发现这个时候除了右下角最大的那个正方形,我们还多了一些正方形可以操作。
情况(3):
【CodeForces - 1080D】Olya and magical square_第3张图片
可以开始总结规律了:
最小操作次数 = 1 + 3 + 7 + … + (2^j - 1)。
令边长为 2^x 的正方形全部剪成 1*1 的操作数为 f(x),有:
f ( x ) = 4 ∗ f ( x − 1 ) + 1 = 1 + 4 + 4 2 + . . . + 4 x − 1 = 4 x − 1 3 f(x) = 4*f(x-1) + 1 = 1+4+4^2+...+4^{x-1}=\dfrac{4^x-1}{3} f(x)=4f(x1)+1=1+4+42+...+4x1=34x1
每一次最大操作次数,都会多一些正方形可以操作。令多出来的正方形个数为 g(x)。则下一次多出来的正方形,除了左上角的那一个正方形对应着 5 个多出来的正方形以外,其他的正方形都对应着 2 个正方形。所以有 g ( x ) = 2 ∗ ( g ( x − 1 ) − 1 ) + 5 g(x)=2*(g(x-1)-1)+5 g(x)=2(g(x1)1)+5

可以发现当 n >= 32 时,f(31) >= 10^18。所以我们可以直接输出情况(1)的解即可。

@Code@

构造类型的题要说清楚真的好难……
如果大家还保有疑惑的,还是留言在下面问我吧,我会尽力解疑的。

#include
typedef long long ll;
ll pow(ll b, int p) {
	ll ret = 1;
	while( p ) {
		if( p & 1 ) ret = ret*b;
		b = b*b;
		p >>= 1;
	}
	return ret;
}
ll GetTotal(int b) {
	return (pow(4, b) - 1)/3;
}
void solve() {
	int n; ll k;
	scanf("%d%lld", &n, &k);
	if( n >= 32 ) {
		printf("YES %d\n", n-1);
		return ;
	}
	else {
		ll p = 0, q = 0, tmp = 1;
		for(int i=n-1;i>=0;i--) {
			q += (1LL<<(n-i)) - 1;
			p += 1LL*tmp*GetTotal(i);
			tmp = (tmp-1)*2 + 5;
			if( q <= k && q >= k-p ) {
				printf("YES %d\n", i);
				return ;
			}
			else if( q > k ) break;
		}
		puts("NO");
	}
}
int main() {
	int t;
	scanf("%d", &t);
	for(int i=1;i<=t;i++) solve();
}

@End@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

你可能感兴趣的:(@那些日子做过的网赛!@,--Codeforces!--,@常用技巧看这里!@,--构造题目!--)