线段树(EOJ Monthly 2019.3 思维 DFS+可行性剪枝)

题目链接:

HDU:Solve this interesting problem

EOJ:线段树

 

题意:

给定一个区间 [l,r] ,求最小的 n ,使得根节点为 [1,n] 的线段树中某个节点为这个区间。若无解,输出-1。

 

思路:

从给定的区间 [l,r] 开始,向上dfs,有4种可能:

该区间为其父节点的左节点:

则其父节点代表的区间为 [l,2*r-l] 或 [l,2*r-l+1] 。

该区间为其父节点的右节点:

则其父节点代表的区间为 [2*l-r-2,r] 或 [2*l-r-1,r] 。

每个有两种可能是因为线段树区间一分为二时,中间的mid是向下取整的,所以会有奇偶两种情况。

这种做法可行是因为有一个条件\left \lfloor \frac{l}{r-l+1} \right \rfloor\leq 1000 。

每向上dfs一层,区间大小 r-l+1 增加一倍(*2),所以左边那个式子的值会 /2 ,当值为1时,就不能再递归下去了,所以最多递归10层。复杂度为 O(4^{10}) 。

可行性剪枝:

1.  r≥ans ( r 是当前的做区间, ans 是当前已经得到的可能最小答案 )时,直接 return ;

2.  r≥2e9 时,直接 return ;

3.  l<1 时 ,直接 return ;

4.  l==r 时,直接特判 ;

5.  l

 

Code:(EOJ)

#include
using namespace std;

typedef long long ll;

const int MAX = 1e6 + 10;

ll ans;

void dfs(ll l, ll r)
{
	if (r > ans)	return;
	if (r > 2e9)	return;
	if (l < 1)	return;
	if (l == 1 || l == r) {
		ans = min(ans, r);
		return;
	}
	if (l < r - l + 1)	return;
	if (2 * r - l >= l)
		dfs(l, 2 * r - l);
	if (2 * r - l + 1 >= l)
		dfs(l, 2 * r - l + 1);
	if (2 * l - r - 2 >= 1)
		dfs(2 * l - r - 2, r);
	if (2 * l - r - 1 >= 1)
		dfs(2 * l - r - 1, r);
	return;
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		ll l, r;
		scanf("%lld%lld", &l, &r);
		ans = 1e10;
		dfs(l, r);
		if (ans == 1e10) {
			printf("-1\n");
		}
		else {
			printf("%lld\n", ans);
		}
	}
	return 0;
}

 

你可能感兴趣的:(ACM-日常训练)