Bzoj 3165 [Heoi2013]Segment题解
3165: [Heoi2013]Segment
Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 668 Solved: 276
[Submit][Status][Discuss]
Description
要求在平面直角坐标系下维护两个操作:
1.在平面上加入一条线段。记第i条被插入的线段的标号为i。
2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。
Input
第一行一个整数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。
Output
对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的线段,答案为0。
Sample Input
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5
Sample Output
0 3
HINT
对于100%的数据,1 ≤ n ≤ 10^5 , 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。
这道题用到了李超线段树,由于不会,就上网强行看了一波题解+标程,然后一脸蒙蔽的打完了。
首先我们可以注意到x的数据范围比y要明显小得多,那么我们可以根据x建线段树,每一个节点代表一个x值,维护的内容就是当前区间内的最靠上的线段。
到这里可能有些人会和博主有一样的疑问,对于一个区间,他每个点的最优解可能来自多条直线,我们该怎么处理呢?这就是李超线段树强悍的地方了,对于这种情况我们不会因为到达当前要修改的位置而停止,而是接着向他的儿子们转移,这样,我们早晚可以让这条线段成为完全的最优解。
对于询问,李超线段树也和平常线段树不一样,首先对于无斜率的线段由于只可能有一个最优解,我们不必再把它放到线段树里面,而是单独记录,然后就从上往下进行查询,查到对应的单点为止,对于我们在路上碰到的节点的最优解我们都可以对我们的答案进行更新,因为他们都覆盖了目标节点。最后直接输出即可。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 #define N 40005 10 #define M 100005 11 using namespace std; 12 int n,p1=39989,p2=1000000000,la; 13 struct no 14 { 15 int left,right,mid; 16 int id; 17 }node[N*4]; 18 struct li 19 { 20 int x1,x2,y1,y2; 21 }line[M]; 22 void build(int left,int right,int x) 23 { 24 node[x].left=left,node[x].right=right; 25 if(left==right) 26 return; 27 int mid=(left+right)/2; 28 node[x].mid=mid; 29 build(left,mid,2*x); 30 build(mid+1,right,2*x+1); 31 } 32 double get_k(li a) 33 { 34 return double(a.y2-a.y1)/double(a.x2-a.x1); 35 } 36 int lmax[N][2],zz; 37 void add(int left,int right,int x,int id) 38 { 39 40 if(!node[x].left)return;//为了防止到了单点还继续向下更新 41 if(node[x].left==left&&node[x].right==right) 42 { 43 if(!node[x].id)node[x].id=id; 44 else 45 { 46 double k1=get_k(line[node[x].id]); 47 double k2=get_k(line[id]); 48 double y1=double(line[node[x].id].y1)-k1*double(line[node[x].id].x1)+k1*left,y2=line[node[x].id].y1-k1*double(line[node[x].id].x1)+k1*right; 49 double y3=double(line[id].y1)-k2*double(line[id].x1)+k2*left,y4=line[id].y1-k2*double(line[id].x1)+k2*right; 50 if(y1>=y3&&y2>=y4) 51 return; 52 else if(y1<=y3&y2<=y4) 53 node[x].id=id; 54 else 55 { 56 int mid=node[x].mid; 57 add(left,mid,x*2,id); 58 add(mid+1,right,2*x+1,id); 59 } 60 } 61 return; 62 } 63 int mid=node[x].mid; 64 if(left>mid) 65 add(left,right,2*x+1,id); 66 else if(right<=mid) 67 add(left,right,2*x,id); 68 else 69 add(left,mid,2*x,id),add(mid+1,right,2*x+1,id); 70 } 71 int ans; 72 double mx; 73 void get(int to,int x) 74 { 75 if(node[x].id) 76 { 77 double k=get_k(line[node[x].id]); 78 double y=double(line[node[x].id].y1)-k*double(line[node[x].id].x1)+k*to; 79 if(mx<y) 80 { 81 mx=y; 82 ans=node[x].id; 83 } 84 } 85 if(node[x].left==node[x].right)return; 86 int mid=node[x].mid; 87 if(to>mid)get(to,2*x+1); 88 else get(to,2*x); 89 } 90 int main() 91 { 92 memset(lmax,-0x7f,sizeof(lmax)); 93 scanf("%d",&n); 94 build(1,p1,1); 95 for(int i=1;i<=n;i++) 96 { 97 int t; 98 scanf("%d",&t); 99 if(t==1) 100 { 101 int x1,x2,y1,y2; 102 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 103 x1=(x1+la-1)%p1+1,y1=(y1+la-1)%p2+1; 104 x2=(x2+la-1)%p1+1,y2=(y2+la-1)%p2+1; 105 zz++; 106 line[zz].x1=x1,line[zz].x2=x2,line[zz].y1=y1,line[zz].y2=y2; 107 if(x1==x2) 108 { 109 if(y1>y2) swap(y1,y2); 110 if(lmax[x1][0]<y2) lmax[x1][1]=zz,lmax[x1][0]=y2; 111 } 112 else 113 { 114 if(x1>x2)swap(x1,x2);//不要忘记左右端点大小顺序 115 add(x1,x2,1,zz); 116 } 117 } 118 else 119 { 120 int x; 121 scanf("%d",&x); 122 x=(x+la-1)%p1+1; 123 mx=lmax[x][0]; 124 ans=lmax[x][1]; 125 if(ans==lmax[0][0])ans=0; 126 get(x,1); 127 printf("%d\n",ans); 128 la=ans; 129 } 130 } 131 return 0; 132 }