【 题 意 】 : \color{blue}{【题意】:} 【题意】: 你的老板
需要你帮忙浇花。给出 N N N滴水
的坐标, y y y表示水滴的高度, x x x表示它下落到x轴的位置。
每滴水以每秒 1 1 1个单位长度的速度下落。你需要把花盆放在 x x x轴上的某个位置,使得从被花盆接着的第 1 1 1滴水开始,到被花盆接着的最后 1 1 1滴水结束,之间的时间差至少为 D D D。
我们认为,只要水滴落到 x x x轴上,与花盆的边沿对齐,就认为被接住。给出 N N N滴水的坐标和 D D D的大小,请算出最小的花盆的宽度 W W W。
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 首先,这题有明显的单调性:如果花盆的长度为 p p p且符合条件,那么如果花盆的长度 > p >p >p,一定也符合条件。
既然符合单调性,就一定可以二分求解。假设我们已经二分出了一个需要判断的解 m i d mid mid,思考如何判断它算法满足条件。
必要的,我们肯定要知道现在的花盆位置是多少。这很简单,直接用枚举计算即可。
更必要的,我们肯定要知道哪些水滴可以被我们接到。我们当然希望这些水滴是一个完整的区间,这样我们只需求出左端点和右端点就确定了所有我们可以接到的水滴。为了完成这一伟大的任务,我们可以在一开始的时候就将所有的水滴按其 y y y坐标从小到大排序。这样以后,我们就可以用尺取法求出哪些水滴可以被我们接到。
好的,假设我们已经知道我们可以接到区间 [ l , r ] [l,r] [l,r]中的水滴。那么答案就是 [ l , r ] [l,r] [l,r]中水滴掉落时间的最大值减去最小值。这显然可以用ST算法完成。
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const int N=1e5+100,LogN=22;
struct water_drop{
int position,times;
void read_itself(){
position=read();
times=read();
}
bool operator < (water_drop c) const{
return position<c.position;
}
}a[N];
struct ST_algorithm{
int fmax[N][LogN],fmin[N][LogN],Log[N];
void init_ST(int n){
Log[0]=-1;
for(int i=1;i<=n;i++){
fmax[i][0]=a[i].times;
fmin[i][0]=fmax[i][0];
Log[i]=Log[i>>1]+1;
}
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++){
fmax[i][j]=max(fmax[i][j-1],fmax[i+(1<<j-1)][j-1]);
fmin[i][j]=min(fmin[i][j-1],fmin[i+(1<<j-1)][j-1]);
}
}
int query_max(int x,int y){
register int s=Log[y-x+1];
return max(fmax[x][s],fmax[y-(1<<s)+1][s]);
}
int query_min(int x,int y){
register int s=Log[y-x+1];
return min(fmin[x][s],fmin[y-(1<<s)+1][s]);
}
}ST;int n,D,l,r,mid,ans;
bool check(int mid){
register int l=1,r=1;
for(int j=mid;j<=a[n].position;j++){
register int i=j-mid;
while (l<=n&&a[l].position<i) l++;
while (r<n&&a[r+1].position<=j) r++;
if (l>r) return false;
if (ST.query_max(l,r)-ST.query_min(l,r)>=D)
return true;
}
return false;
}
int main(){
freopen("t1.in","r",stdin);
n=read();D=read();
for(int i=1;i<=n;i++)
a[i].read_itself();
sort(a+1,a+n+1);
ST.init_ST(n);
l=0;r=a[n].position;ans=-1;
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}
【 题 解 】 : \color{blue}{【题解】:} 【题解】:
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 很简单,唯一的难度其实在于如何判断一颗二叉树是否为对称二叉树。
我们可以用深搜判断,假设 c h e c k ( l , r ) check(l,r) check(l,r)表示判断以 l l l为根的二叉树和以 r r r为根的二叉树是否对称。
我们可以这样判断,首先判断 l l l和 r r r的权值是否相等,然后判断 l l l的左子树和 r r r的右子树是否对称,再判断 l l l的右子树和 r r r的左子树是否对称。如果都满足,就对称,否则不对称。
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
#define ll long long
const int N=1e6+100;
int n,ans;bool flag;
int value[N],l[N],r[N];
int cnt(int u){
register int sum=0;
if (~l[u]) sum+=cnt(l[u]);
if (~r[u]) sum+=cnt(r[u]);
return sum+1;
}
void check(int x,int y){
if (x==-1&&y==-1) return;
if (x==-1||y==-1||value[x]!=value[y]){
flag=false;return;
}
check(l[x],r[y]);
check(r[x],l[y]);
}
int main(){
scanf("%d",&n);ans=1;
for(int i=1;i<=n;i++)
scanf("%d",&value[i]);
for(int i=1;i<=n;i++)
scanf("%d%d",&l[i],&r[i]);
for(int i=1;i<=n;i++){
if (l[i]!=-1&&r[i]!=-1&&value[l[i]]==value[r[i]]){
flag=true;check(l[i],r[i]);
if (flag) ans=max(ans,cnt(i));
}
}
printf("%d",ans);
return 0;
}