问题 D: [Heoi2013]Segment
时间限制: 4 Sec 内存限制: 256 MB
题目描述
要求在平面直角坐标系下维护两个操作:
1.在平面上加入一条线段。记第i条被插入的线段的标号为i。
2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。
输入
第一行一个整数n,表示共n 个操作。
接下来n行,每行第一个数为0或1。
若该数为 0,则后面跟着一个正整数 k,表示询问与直线
x = ((k +lastans–1)%39989+1)相交的线段中交点(包括在端点相交的情形)最靠上的线段的编号,其中%表示取余。若某条线段为直线的一部分,则视作直线与线段交于该线段y坐标最大处。若有多条线段符合要求,输出编号最小的线段的编号。
若该数为 1,则后面跟着四个正整数 x0, y0, x 1, y 1,表示插入一条两个端点为
((x0+lastans-1)%39989+1,(y0+lastans-1)%10^9+1)和((x
1+lastans-1)%39989+1,(y1+lastans-1)%10^9+1) 的线段。
其中lastans为上一次询问的答案。初始时lastans=0。
输出
对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的线段,答案为0。
样例输入
6
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5
样例输出
2
0 3
提示
对于100%的数据,1 ≤ n ≤ 10^5 , 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。
其实这道题就是个板子,李超线段树。(这个板子就是为了解决这个问题,只不过板子太难了而已,汗颜)
就题论算法把,李超线段树用来处理向一个区间加有斜率的线段,之后判断某位置权值最大的线段是哪条之类的问题。而最重要的是多了的insert函数。当分到的区间已属于这条线段覆盖的区间是,就要进行更神奇的操作了,去比较当前节点所存的最优线段。先附上代码。
void insert(int l,int r,int x,int k)
{
if(!t[x].h)t[x].h=k;
if(cmp(t[x].h,k,l))swap(t[x].h,k);
if(l==r||a[t[x].h].k==a[k].k)return;
int mid=(l+r)/2;double g=(double)(a[t[x].h].b-a[k].b)/(a[k].k-a[t[x].h].k);
if(g<l||g>r)return;
if(g<=mid)insert(l,mid,x*2,t[x].h),t[x].h=k;
else insert(mid+1,r,x*2+1,k);
}
因为比较分为两种情况,完全压制和在区间内有交点。完全压制不动或直接修改后返回就好了。而对于相交的就必须求出交点,根据交点的位置(其实也就是判断那条线段在交点那一边处于优势)去修改子区间。
对于这段代码理解还是有点困难(本蒟蒻太水,自己口胡。。)要把本区间右侧较大的线段置成本区间最优(我也不是太懂为什么。。望神犇来解释),若不懂,手动模拟一下过程即可。
还要注意一些细节:直线斜率是否存在,以及两条线是否平行。。(RE到死)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 101000
#define ll long long
using namespace std;
struct node
{
int l,r,id;double k,b;
}a[N];
struct tree{int l,r,h;}t[50000*4];
int n,m;
void build(int l,int r,int x)
{
t[x].l=l;t[x].r=r;
if(l==r){t[x].h=0;return;}
int mid=(l+r)/2;
build(l,mid,x*2);
build(mid+1,r,x*2+1);
}
bool cmp(int x,int y,double l)
{
if(!x)return 1;
double l1=a[x].k*l+a[x].b,l2=a[y].k*l+a[y].b;
return l1!=l2?l1<l2:x<y;
}
int q(int k,int x)
{
if(t[x].l==t[x].r)return t[x].h;
int mid=(t[x].l+t[x].r)/2,tmp;
if(k<=mid)tmp=q(k,x*2);
else tmp=q(k,x*2+1);
return cmp(t[x].h,tmp,k)?tmp:t[x].h;
}
void insert(int l,int r,int x,int k)
{
if(!t[x].h)t[x].h=k;
if(cmp(t[x].h,k,l))swap(t[x].h,k);
if(l==r||a[t[x].h].k==a[k].k)return;
int mid=(l+r)/2;double g=(double)(a[t[x].h].b-a[k].b)/(a[k].k-a[t[x].h].k);
if(g<l||g>r)return;
if(g<=mid)insert(l,mid,x*2,t[x].h),t[x].h=k;
else insert(mid+1,r,x*2+1,k);
}
void c(int k,int x)
{
if(t[x].l>=a[k].l&&t[x].r<=a[k].r)
{insert(t[x].l,t[x].r,x,k);return;}
int mid=(t[x].l+t[x].r)/2;
if(a[k].l<=mid)c(k,x*2);
if(a[k].r>mid)c(k,x*2+1);
}
int main()
{
scanf("%d",&n);int x1,x2,y1,y2,z,ans=0;
build(1,50000,1);
while(n--)
{
scanf("%d",&z);
if(z==0)
{
scanf("%d",&x1);x1=(x1+ans-1)%39989+1;//out<<x1<<endl;
ans=q(x1,1);
printf("%d
",ans);
}
else
{
m++;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1=(x1+ans-1)%39989+1;
x2=(x2+ans-1)%39989+1;
y1=(y1+ans-1)%1000000000+1;
y2=(y2+ans-1)%1000000000+1;
//cout<<x1<<" "<<x2<<" "<<y1<<" "<<y2<<endl;
if(x1>x2)swap(x1,x2),swap(y1,y2);
if(x1==x2)a[m].k=0,a[m].b=max(y1,y2);
else
{
a[m].k=(double)(y1-y2)/(x1-x2);
a[m].b=(double)y1-a[m].k*x1;
}
a[m].l=x1;a[m].r=x2;a[m].id=m;
c(m,1);
}
}
}