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