題意:
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);
}
}