码迷,mamicode.com
首页 > 其他好文 > 详细

Bzoj 3165: [Heoi2013]Segment

时间:2018-02-24 23:03:04      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:min   namespace   告诉   read   zoj   div   esc   ...   name   

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)和((x1+lastans-1)%39989+1,(y1+lastans-1)%10^9+1) 的线段。 其中lastans为上一次询问的答案。初始时lastans=0。

Output

对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的线段,答案为0。

Sample Input

6

1 8 5 10 8

1 6 7 2 6

0 2

0 9

1 4 7 6 7

0 5

Sample Output

2

0 3

HINT

对于100%的数据,1 ≤ n ≤ 10^5 , 1 ≤  k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。

 

题的名字已经告诉了做法...线段树,线段树坐标代表线段的x,每个节点记录把当前区间完全覆盖并且最靠上的线段,如果有两条线段相交就随便选一条,另一条看成两条线段向下递归,注意各种细节,但是貌似这题不卡精度。每次查询取每个经过区间的最优解,因为对于一个点,每个包含这个点的区间都有可能是最优解。复杂度我并不会证...貌似是O(nlog^2n)。

下面是代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 using namespace std;
 7 int st,ed,cnt,ans,imod=39989;
 8 double mx,hi[40000];
 9 struct node{
10     int x,y,xx,yy;
11     double k,b;
12     void read(){
13         scanf("%d%d%d%d",&x,&y,&xx,&yy);
14         x=(x+ans-1)%imod+1;
15         xx=(xx+ans-1)%imod+1;
16         y=(y+ans-1)%1000000000+1;
17         yy=(yy+ans-1)%1000000000+1;
18         if(x>xx) swap(x,xx),swap(y,yy);
19         if(x!=xx){
20             k=double(yy-y)/(double)(xx-x);
21             b=(double)y-k*(double)x;
22         }
23         else if(y>yy) swap(y,yy);
24     }
25     double f(double pos){return k*pos+b;}
26     
27 }in[100010];
28 int tree[160000],id[40000];
29 void find(int l,int r,int pos){
30     if(tree[pos]){
31         if(in[tree[pos]].f(st)>mx){
32             mx=in[tree[pos]].f(st);
33             ans=tree[pos];
34         }
35         else if(in[tree[pos]].f(st)==mx){
36             ans=min(ans,tree[pos]);
37         }
38     }
39     if(l==r){
40         if(mx<hi[st]) ans=id[st];
41         else if(mx==hi[st]) ans=min(ans,id[st]);
42         return;
43     }
44     int mid=(l+r)/2,lson=pos*2,rson=pos*2+1;
45     if(st<=mid) find(l,mid,lson);
46     else find(mid+1,r,rson);
47     return;
48 }
49 void insert(int l,int r,int pos){
50     if(st<=l&&r<=ed){
51         if(tree[pos]==0){
52             tree[pos]=cnt;
53             return;
54         }
55         double l1=in[cnt].f(l),l2=in[tree[pos]].f(l),r1=in[cnt].f(r),r2=in[tree[pos]].f(r);
56         int mid=(l+r)/2,lson=pos*2,rson=pos*2+1;
57         if(l1>l2&&r1>r2){tree[pos]=cnt;return;}
58         if(l1<=l2&&r1<=r2) return;
59         insert(l,mid,lson);
60         insert(mid+1,r,rson);
61     }
62     int mid=(l+r)/2,lson=pos*2,rson=pos*2+1;
63     if(st<=mid) insert(l,mid,lson);
64     if(mid<ed) insert(mid+1,r,rson);
65     return;
66 }
67 int main()
68 {
69     int n,i,t;
70     scanf("%d",&n);
71     for(i=1;i<=n;i++){
72         scanf("%d",&t);
73         if(t==0){
74             scanf("%d",&st);
75             st=(st+ans-1)%imod+1;
76             mx=-1000.0,ans=0;
77             find(1,imod,1);
78             printf("%d\n",ans);
79         }
80         else{
81             in[++cnt].read();
82             st=in[cnt].x,ed=in[cnt].xx;
83             if(st==ed){
84                 if(hi[st]<in[cnt].yy){
85                     hi[st]=in[cnt].yy;
86                     id[st]=cnt;
87                 }
88             }
89             else insert(1,imod,1);
90         }
91     }
92 }

 

Bzoj 3165: [Heoi2013]Segment

标签:min   namespace   告诉   read   zoj   div   esc   ...   name   

原文地址:https://www.cnblogs.com/pncbf/p/8467813.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!