题目连接
大意:给你n个水平线段,m个垂直线段,让你计算加号的最长是多少。
其中加号的长度定义为:
ps:在组队训练时,一直想怎么直接确定最大值。。。。没有往二分上面去想。
思路:二分答案。
具体做法:设此时check的值为d,那么有意义的水平和竖直线段的长度肯定>=2d.,而且对一个长度>=2d的水平线段
(
x
s
,
x
t
,
y
)
,
x
s
<
=
x
t
(x_s,x_t,y),x_s<=x_t
(xs,xt,y),xs<=xt,显然,竖直线的水平坐标x必然要在
[
x
s
+
d
,
x
t
−
d
]
[x_s+d,x_t-d]
[xs+d,xt−d]才可能使得有一组水平竖直线的加号长度>=d,我们称
[
x
s
+
d
,
x
t
−
d
]
[x_s+d,x_t-d]
[xs+d,xt−d]为此横线的合法区间,如果某个竖线a的横坐标在此合法区间内,我们称之匹配。
那我们将所有长度>=2*d的竖直线段按照横坐标递增排序。然后依次枚举,并用multiset维护和此竖线匹配的横线的y坐标,然后在multiset里检查是否有一个值介于
[
y
s
+
d
,
y
t
−
d
]
,
y
s
<
=
y
t
[y_s+d,y_t-d],y_s<=y_t
[ys+d,yt−d],ys<=yt之中即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int n,m,_;
struct uzi{
int a,b,c;
bool operator <(const uzi & t)const{
if(a==t.a)return b<t.b;
return a<t.a;
}
}p[N],q[N];
int c(int x){
vector<uzi>v;
for(int i=1;i<=n;i++){
if(p[i].b-p[i].a>=2*x){
v.pb({p[i].a+x,1,p[i].c});
v.pb({p[i].b-x,3,p[i].c});
}
}
for(int i=1;i<=m;i++){
if(q[i].b-q[i].a>=2*x){
v.pb({q[i].c,2,i});
}
}
sort(v.begin(),v.end());
multiset<int>s;
for(auto k:v){
if(k.b==1)s.insert(k.c);
else if(k.b==3)s.erase(s.find(k.c));
else{
int nx=k.c;
auto f=s.lower_bound(q[nx].a+x);
if(f!=s.end() && *f+x<=q[nx].b)return 1;
}
}
return 0;
}
int main() {
for(scanf("%d",&_);_;_--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
if(p[i].a>p[i].b)swap(p[i].a,p[i].b);
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c);
if(q[i].a>q[i].b)swap(q[i].a,q[i].b);
}
int l=0,r=250000,ans=0;
while(l<=r){
int mid=l+r>>1;
if(c(mid))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}