题目连接
大意:给你n个水平线段,m个垂直线段,让你计算加号的最长是多少。
其中加号的长度定义为:
ps:在组队训练时,一直想怎么直接确定最大值。。。。没有往二分上面去想。
思路:二分答案。
具体做法:设此时check的值为d,那么有意义的水平和竖直线段的长度肯定>=2d.,而且对一个长度>=2d的水平线段,显然,竖直线的水平坐标x必然要在
才可能使得有一组水平竖直线的加号长度>=d,我们称
为此横线的合法区间,如果某个竖线a的横坐标在此合法区间内,我们称之匹配。
那我们将所有长度>=2*d的竖直线段按照横坐标递增排序。然后依次枚举,并用multiset维护和此竖线匹配的横线的y坐标,然后在multiset里检查是否有一个值介于之中即可。
#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; }