题目大意:
题目链接:https://jzoj.net/senior/#main/show/4208
这是一个线段树建树的代码。
void buildtree(int k1,int l,int r){
if (l==r) return;
int mid=(l+r)/2;
buildtree(k1*2,l,mid); buildtree(k1*2+1,mid+1,r);
}
若一个根节点为[0,n)(n∈0∼lim)[0,n)(n\in0\sim lim)[0,n)(n∈0∼lim)的线段树包含了区间l,rl,rl,r,求nnn的最小值、
思路:
对于一个区间l,rl,rl,r,长度为lenlenlen,它的父节点只可能有以下四个:
- [l,r+len−1)[l,r+len-1)[l,r+len−1)
- [l,r+len)[l,r+len)[l,r+len)
- [l−len,r)[l-len,r)[l−len,r)
- [l−len−1,r)[l-len-1,r)[l−len−1,r)
那么就从l,rl,rl,r往上搜索,取最优答案即可。
然后就能愉快的拿到303030分。
这样的时间复杂度太高了。考虑剪枝。
我们发现,如果lll不是000的话,那么就可以扩张到区间[l,r+len−1)[l,r+len-1)[l,r+len−1)或[l,r+len)[l,r+len)[l,r+len)。那么此时lll还是不等于000,所以只有往左扩张才可能使lll为000。但是扩张一次就相当于长度乘222,也就是往左扩张2len2len2len。但是如果l<2lenl<2lenl<2len,那么就无法扩张,所以也就无法扩张成[l,r+len−1)[l,r+len-1)[l,r+len−1)或[l,r+len)[l,r+len)[l,r+len)。
这样就可以过了。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
ll L,R,lim,ans,Read;
int T;
char ch;
ll read()
{
Read=0;
ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9')
Read=(Read<<3)+(Read<<1)+ch-48,ch=getchar();
return Read;
}
ll write(ll x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
void dfs(ll l,ll r)
{
if (l<0||r>lim) return; //超过范围
if (!l) //满足要求
{
ans=min(ans,r);
return;
}
ll len=r-l+1;
if (l>=len*2-1) dfs(l,r+len-1);
if (l>=len*2) dfs(l,r+len); //剪枝
dfs(l-len,r);
dfs(l-len-1,r);
}
int main()
{
T=read();
while (T--)
{
L=read(),R=read(),lim=read();
if (L==R) write(L);
else
{
ans=lim+1;
dfs(L,R);
if (ans<lim) write(ans);
else putchar('-'),putchar('1');
}
putchar(10);
}
return 0;
}