题目大意
只要调用buildtree(1,0,n) 就可以得到一颗线段树了。显然,一颗线段树一共有O(n) 个节点,因为每一个节点都代表了一个不同的区间,所以线段树上一共出现了O(n) 个不同的区间。
现在给了你一个区间[l, r],他想要你告诉他一个最小的n 使得区间[l; r] 出现在了用buildtree(1,0,n) 建出来的线段树中。
题目解析
看到给定的buildtree(1,0,n),一种就简单的解法就是枚举n(r~lim),直接调用此函数,顺推下去,判断是否符合。(预期得分:30或0)
既然顺推不行,要枚举n,会爆,那就逆推
深搜(从答案[l,r]往[0,lim]搜)
[l,r]->[l,2 * r-l],
——[l,2 * r-l+1],
——[2 * (l-1)-r,r],
——[2 * (l-1)-r+1,r]
当 l==0 时,就更新ans
然后就要加剪枝
一:显而易见,当超出限制时,即 r>lim or l<0 ,则return
二:当 l>r or r>ans 时,则return
三:接着就是特判了
1、l== r or l==0 则可以直接输出r
2、经过推理和尝试, 发现 2*l<=r ,可直接输出-1;ans<=x+y,所以lim=min(lim,l+r)
自己感性理解一下
代码
#include<bits/stdc++.h>
using namespace std;
int T,x,y,lim,ans;
void dfs(int l,int r)
{
if(r>lim||l<0||l>r||r>ans) return;
if(l==0)
{
ans=min(ans,r);
return;
}
dfs(l,r*2-l);
dfs(l,r*2+1-l);
dfs((l-1)*2-r,r);
dfs((l-1)*2+1-r,r);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&x,&y,&lim);
lim=min(lim,x+y);
if(x==y||x==0)
{
cout<<y<<endl;
continue;
}
if(2*x<=y)
{
cout<<-1<<endl;
continue;
}
ans=2e9+7;
dfs(x,y);
if(ans!=2e9+7)
cout<<ans<<endl;
else
cout<<-1<<endl;
}
}