打湖南多校遇见的这套题 (Gym 100543B) 我们队做了四个水题 就不会了 其他题感觉难度都有所提高 还是得加强训练突破中档题
本题链接:https://vjudge.net/contest/371507#problem/B
先说明的是bzoj上似乎是单样例 UVA和cf上是多样例 另外个人建议拿cf交 UVA经常崩 什么对的代码提交wa的 也遇到过好几次了
本题的思路来自大佬:https://blog.csdn.net/ezoiHQM/article/details/82154368
线段树维护凸包的题目先前 我也没做过 也就依葫芦画瓢了
首先我们要清楚一个定理(不知道是不是定理啊 口胡一下):如果一条射线和一个凸包相交,那么肯定和构成这凸包的线段相交
对于区间L到R 线段树维护这个区间构成的凸包 然后对于每条射线 我们用二分的方式去看它和凸包有没有交点 在线段树上先访问左子树在访问右子树 这样能保证是第一条满足条件的线段
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
struct Point{
int x,y;
Point(int _x,int _y){
x=_x;y=_y;
}
Point(){}
ll operator *(Point b){
return 1ll*x*b.y-1ll*y*b.x;
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
}p[N];
struct seg_tree_convex{
vector<Point>c;
void clear(){
c.clear();
}
void insert(Point a){
for(int j=c.size();j>1&&(a-c[j-2])*(c[j-1]-c[j-2])<=0;j--)
c.pop_back();
c.push_back(a);
return;
}
bool check(Point a,Point b){
int l=0,r=c.size()-2;
while(l<r){
int mid=(l+r)>>1;
if((c[mid]-a)*(b-a)<(c[mid+1]-a)*(b-a))
r=mid;
else l=mid+1;
}
return (c[l]-a)*(b-a)<0||(c[l+1]-a)*(b-a)<0;
}
}tr[N<<2];
void build(int id,int l,int r){
tr[id].clear();
if(l==r){
tr[id].insert(p[l]);
tr[id].insert(p[l+1]);
return;
}
for(int i=l;i<=r+1;i++)
tr[id].insert(p[i]);
int mid = l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
int query(int o,int l,int r,int L,int R,Point a,Point b){
if(L<=l&&r<=R){
if(!tr[o].check(a,b))
return 0;
if(l==r)
return l;
}
int mid=(l+r)>>1,ret=0;
if(L<=mid)
ret=query(o<<1,l,mid,L,R,a,b);
if(!ret&&R>mid)
ret=query(o<<1|1,mid+1,r,L,R,a,b);
return ret;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d%d",&p[i].x,&p[i].y);
build(1,1,n-1);
for(int i = 1; i <= n-1; i++)
if(i!=n-1)
printf("%d ",query(1,1,n-1,i+1,n-1,p[i],p[i+1]));
else printf("%d\n",query(1,1,n-1,i+1,n-1,p[i],p[i+1]));
}
return 0;
}