[Heoi2013]Segment

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。
1 ≤ n ≤ 10^5 , 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。

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

标记永久化,不过这题插入的是一条条线段,其实也没什么关系,稍微判断下就好了。不过需要记得判断斜率无穷大,然后基本上就没啥了。

一道比这题简单点的题,[JSOI2008]Blue Mary开公司

两题基本相似,小细节自己画个图理解下就好

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+'0');
}
const int N=1e5,px=39989,py=1e9;
double K[N+10],B[N+10];
int n,lastans,cnt;
bool check(int x,int y,int pos){
    if (!x) return 1;
    double l1=K[x]*pos+B[x],l2=K[y]*pos+B[y];
    return l1!=l2?l1<l2:x<y;
}
struct Segment{
    #define ls (p<<1)
    #define rs (p<<1|1)
    int tree[N*4+10];
    void insert(int p,int l,int r,int t){//判断和“Blue Mary开公司”稍有不同,不过读者们可以画个图,便可以马上理解了
        if (!tree[p])   tree[p]=t;
        if (check(tree[p],t,l)) swap(tree[p],t);
        if (l==r||K[tree[p]]==K[t]) return;
        double g=1.0*(B[tree[p]]-B[t])/(K[t]-K[tree[p]]);
        if (g<l||g>r)   return;
        int mid=(l+r)>>1;
        if (g<=mid) insert(ls,l,mid,tree[p]),tree[p]=t;
        if (g>mid)  insert(rs,mid+1,r,t);
    }
    int query(int p,int l,int r,int t){
        if (l==r)   return tree[p];
        int mid=(l+r)>>1,ans;
        if (t<=mid) ans=query(ls,l,mid,t);
        if (t>mid)  ans=query(rs,mid+1,r,t);
        return check(tree[p],ans,t)?ans:tree[p];
    }
}Tree;
void change(int p,int l,int r,int x,int y,int t){//由外部修改转移到线段树上修改
    if (x<=l&&r<=y){Tree.insert(p,l,r,t);return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(p<<1,l,mid,x,y,t);
    if (y>mid)  change(p<<1|1,mid+1,r,x,y,t);
}
int main(){
    n=read(),lastans=0;
    for (int i=1;i<=n;i++){
        int t=read();
        if (t){
            cnt++;
            int x1=read(),y1=read(),x2=read(),y2=read();
            x1=(x1+lastans-1)%px+1,y1=(y1+lastans-1)%py+1;
            x2=(x2+lastans-1)%px+1,y2=(y2+lastans-1)%py+1;
            if (x1>x2)  swap(x1,x2),swap(y1,y2);
            if (x1==x2) K[cnt]=0,B[cnt]=max(y1,y2);
            else{
                K[cnt]=1.0*(y2-y1)/(x2-x1);
                B[cnt]=1.0*y1-K[cnt]*x1;
            }
            change(1,1,N,x1,x2,cnt);
        }else{
            int x=(read()+lastans-1)%px+1;
            lastans=Tree.query(1,1,N,x);
            printf("%d\n",lastans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值