CF1469D Ceil Divisions 题解

Content

你有一个长度为 \(n\) 的数组 \(a\),初始时,\(\forall i\in[1,n]\)\(a_i=i\)

每次操作选择两个数 \(x,y(1\leqslant x,y\leqslant n,x\neq y)\),然后将 \(a_x\) 转换为 \(\left\lceil\dfrac{a_x}{a_y}\right\rceil\)。你需要执行不超过 \(n+5\) 次操作将数组 \(a\) 转换为一个包含 \(n-1\)\(1\)\(1\)\(2\) 的数组。请给出一个构造方案。

数据范围:\(t\) 组数据,\(1\leqslant t\leqslant 10^3\)\(3\leqslant n,\sum n\leqslant 2\times 10^5\)

Solution

不难想到的做法是,\(\forall i\in[3,n)\),每次将 \(a_i\) 转换为 \(\left\lceil\dfrac{a_i}{a_{i+1}}\right\rceil\)。可以证明,由于 \(a_i<a_{i+1}\),所以转换的结果必然为 \(1\)。然后我们再用剩下的 \(2\) 不断的去除 \(n\) 直到 \(n\) 变成 \(1\) 为止。操作数约为 \(n+\log n\),而 \(\log n\) 最大值显然会超过 \(5\),因此是不可行的。考虑如何优化这个操作方案。

我们发现,拿 \(\left\lceil\sqrt{n}\right\rceil\) 去除 \(n\) 最多仅需 \(2\) 次就可以将 \(n\) 变成 \(1\),因此,我们不妨把 \([\left\lceil\sqrt{n}\right\rceil,n]\)\([\left\lceil\sqrt{\left\lceil\sqrt{n}\right\rceil}\right\rceil,\left\lceil\sqrt{n}\right\rceil]\)\(\dots\) 等部分分成一段(注意 \(1,2\) 不能被分到任何一段中去),对于每一段,我们先把中间的所有元素全拿最后一个元素去除使它门全部都变成 \(1\),然后再去拿最左边的元素去除以最右边的元素 \(2\) 次,即可做到使一段里面的元素全部变成 \(1\)

然后这道题目就可以过了。

Code

namespace Solution {
	const int N = 2e5 + 7;
	int n;
	struct node {int x, y;};
	vector<node> ans;
	
	ii checksq(int x) {
		int sqrtx = sqrt(x);
		return sqrtx * sqrtx == x;
	}
	
    iv Main() {
		MT {
			read(n), ans.clear();
			int cur = sqrt(n) + !checksq(n), precur = n, fl = 0;
			while(precur > 2) {
				F(int, i, cur + 1, precur - 1) ans.push_back((node){i, precur});
				F(int, i, 1, 2) ans.push_back((node){precur, cur});
				precur = cur, cur = sqrt(precur) + !checksq(precur);
			}
			int cnt = ans.size(); println(cnt);
			F(int, i, 0, cnt - 1) printf("%d %d\n", ans[i].x, ans[i].y);
		}
		return;
	}
}
posted @ 2021-12-15 21:13  Eason_AC  阅读(45)  评论(0)    收藏  举报