EOJ Monthly 2019.3 (based on March Selection)(level 3) C. 線段樹(爆搜+可行性剪枝)

題目鏈接

題意:

function build(l, r, x):

   init node x

   if l < r then:

        mid = floor((l + r) / 2)

        build(l, mid, x * 2)

        build(mid + 1, r, x * 2 + 1)

這是建立線段樹的代碼,現在給你一個區間[l,r],讓你在[1,2e9]以內找到最小的n,

使得進行 build(1, n, 1) 操作的時候,線段樹上某一個結點恰好表示該區間

即在build(1, n, 1)操作時,某時刻參數 l 和 r 恰好是給出區間的兩個端點

如果找不到就輸出-1

 

解析:

這道題做的時候沒想出來,往正確的方向想過,但沒有深想

題解就是從[l,r]向上爆搜,這個可行的原因是題目給的一個數據條件[L/(R-L+1)]<=100

這個條件再加上[R-L+1]<=L-1(這個是線段樹的一個性質,可以多畫幾個線段樹就看出來了)

所以這個遞歸的層數最多10層

但是這道題遞歸的方向是有4個,[L,R]可以變成

[L-len,R]

[L-len-1,R]

[L,R+len-1]

[L,R+len]

這樣時間複雜度就達到O(4^10*500),依然會T,我就是在這裏被卡了一天...最後的剪枝想不出來,還一直在想怎麼把

4個方向變成2了..

其實這個剪枝也經常用到了——可行性剪枝:噹噹前區間已經不可能得到小於當前答案ans的解時,就不需要再往下找了

if(r>ans) continue;

這個剪枝其實在找最大團的時候也有用到,一般暴力的算法都會加上這種剪枝來卡時間

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e9;

ll ans;

int dfs(ll l,ll r)
{

	if(l==1) return r;

	ll len=r-l+1;
	if(len>l-1) return -1;
	ll nl=l-len;  //len,len
	//ll ans=N;
	int flag=0;
	if(nl>0)
	{
		ll res=dfs(nl,r);
		if(res!=-1) ans=min(ans,res),flag=1;
	}

	nl=l-len-1;  //len+1,len
	if(nl>0)
	{
		ll res=dfs(nl,r);
		if(res!=-1) ans=min(ans,res),flag=1;
	}

	ll nr;
	nr=r+len-1;  //len,len-1
	if(nr<=ans)
	{
		ll res=dfs(l,nr);
		if(res!=-1) ans=min(ans,res),flag=1;
	}

	nr=r+len; //len,len
	if(nr<=ans)
	{
		ll res=dfs(l,nr);
		if(res!=-1) ans=min(ans,res),flag=1;
	}

	if(!flag) return -1;
	else return ans;


}


int main()
{
	int t;
	scanf("%d",&t);
	while (t--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		ans=N;
		if(l==r) ans=l;
		else ans=dfs(l,r);
		printf("%lld\n",ans);
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章