时间限制: 5 Sec 内存限制: 2048 MB

题目描述
小葱和小绪是一对好朋友,自从小葱11连出了1UR2SR之后,小绪就觉得小葱的人品特别好,于是小绪给小葱出了一道题来测试小葱的人品。
小绪首先在平面上画了N个点,分别是p1,p2,…pn。小绪把这N个点顺次相连,即连接(p1, p2), (P2, P3),…,(pN-1,pN),得到N-1条线段。
之后小绪每次在平面上画出一条直线,然后问小葱这条直线与多少条线段相交。特别地,在线段端点相交算作相交,直线完全覆盖线段时也算作相交。
这样的问题自然难不倒小葱,小葱只需要凭白己的人品用直觉便能给出正确的答案。
小绪想测试小葱的人品究竞有多好.于是他加大了问题的难度:
除了每次询问以外,小绪会不时地将一个新的点P插入到p­i和pi+1之间,然后按照顺序对所有的点重新标记下标。即在pi之后的点的下标会依次增加,而点p会变成新的点pi+1。特别地,点P也可以插入到第一个点之前或最后一个点之后。
人品超级好的小葱依旧能够轻松的给出答案,于是小绪又进一步提高了难度:
每次插入或提问之后,小绪都将操作后的所有线段记录了下来。称作一个历史版本。历史版本T表示在第T次操作后得到的历史版本。
插入新点的操作改为了在某一个历史版本T的基础上,插入一个点P,并得到一个新的历史版本。
小绪对小葱的提问改为了对于一个历史版本T,给出一条直线,询问这条直线会与多少条线段相交。
小葱虽然人品很好但面对这样的问题却也束手无策了他只好找到来参加CTSC的你,请你来帮他解决这个问题。

输入
第一行两个整数N, M, C,表示一开始的点数和总数的操作数,以及数据是否加密。如果C=1,那么代表数据被加密过,每次询问操作中的X0,Y­0,X,Y以及插入操作中的X, Y都是被加密过的数据,你需要将它们异或last_ans从而得到正确的数据,其中last_ans是上一次询问的答案,刚开始时last_ans=0。
接下来N行每行两个整数,其中第i行的两个数表示pi的横坐标和纵坐标。
接下来M行,表示小绪的M次操作,其中第i行(从1开始标号)操作后得到的结果为历史版本i。
如果这个字母为’H’,代表本次操作为一次询问操作:接下来会有五个整数T, X0,Y­0,X,Y,代表在历史版本T的情况下,小绪给出一条经过(X0,Y0),方向为(X,Y)的直线,小葱要回答出它会和多少条线段相交。
如果这个字母为’Z’,代表本次操作为一次插入操作:接下来会有四个数T,i,X,Y,代表小绪在历史版本T的基础上,在p­i后面插入了一个坐标为(X,Y)的点。特别地,如果i=0,表示该点插入在p­1之前。

输出
要求对于每一次询问操作,输出一行一个整数代表小葱应该回答的正确答案。

【样例输入1】
2 3 0
0 0
1 1
H 0 1 0 -1 1
H 1 0 1 1 1
H 2 -1 -1 1 1
【样例输入2】
2 3 0
0 0
2 2
Z 0 0 2
H 0 0 1 1 1
H 1 0 1 1 1

【样例输出1】
1
0
1
【样例输出2】
0
2

【数据规模和约定】
保证每次询问操作的T一定小于等于当前操作的数量,所有输入数据均为整数。
有以下4类特殊数据,它们两两没有交集:
1.对于10%的数据,保证1≤N, M≤1000;
2.对于15%的数据,保证对于第i次操作,T=i-1;
3.对于15%的数据,保证C=0且不存在修改操作;
4.对于15%的数据,对于相同操作,保证Y=0(加密过的数据指解密后的Y),即给出的直线平行于x轴。
以上数据还保证1≤N,M≤5×104 。
对于100%的数据,保证1≤N,M≤105,所有的坐标范围在[-108 , 108]内,且每组数据中所有询问的答案总和不超过106,插入操作的次数不会超过5×104。注竟这些线段可能会互相相交。

来源
ctsc 2015 day1

题解

正解好像是重量平衡树套线段树维护可持久化凸包。
不会(雾)。
然而我们有kd-tree。
对于一些折线段,我们可以用矩形框来表示。
用可持久化treap维护可持久化kd-tree。
如果一条直线与矩形框没有交点,那么这条直线必然与这些折线段没有交点。
如果与矩形框存在交点,则可能与折现折线段有交点,递归判定子矩形即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cstring>
#include<string>
#include<algorithm>
#define ll long long
#define N 100005
using namespace std;
int n,m,tp,pre,cnt,rt[N];ll A,B,C;
int L=2147483647,k=2000000207;
int Rand(){return (k+=(k<<2)+1)&L;}
struct point{int x,y;};
struct node{
  int x1,y1,x2,y2,lc,rc,size;point p,l,r;
  bool operator <(const node &p)const{
    return (ll)Rand()*(size+p.size)<(ll)(size)*L;
  }
}t[N*60];
int get(int x){return x^pre;}

class Kevin_Durant_Tree
{
  void update(int x)
  {
    int lc=t[x].lc,rc=t[x].rc;
    t[x].l=t[x].p;t[x].r=t[x].p;
    t[x].x1=t[x].x2=t[x].p.x;
    t[x].y1=t[x].y2=t[x].p.y;
    t[x].size=t[lc].size+t[rc].size+1;
    if(lc)
    {
      t[x].l=t[lc].l;
      t[x].x1=min(t[x].x1,t[lc].x1);
      t[x].y1=min(t[x].y1,t[lc].y1);
      t[x].x2=max(t[x].x2,t[lc].x2);
      t[x].y2=max(t[x].y2,t[lc].y2);
    }
    if(rc)
    {
      t[x].r=t[rc].r;
      t[x].x1=min(t[x].x1,t[rc].x1);
      t[x].y1=min(t[x].y1,t[rc].y1);
      t[x].x2=max(t[x].x2,t[rc].x2);
      t[x].y2=max(t[x].y2,t[rc].y2);
    }
  }
  public:
  node make(int x,int y)
  {
    node res;
    res.l=res.r=res.p=(point){x,y};
    res.x1=res.x2=x;res.y1=res.y2=y;
    res.size=1;res.lc=res.rc=0;
    return res;
  }
  int merge(int a,int b)
  { 
    if(!a)return b;if(!b)return a;
    int x=++cnt;
    if(t[a]<t[b])t[x]=t[a],t[x].rc=merge(t[a].rc,b);
    else t[x]=t[b],t[x].lc=merge(a,t[b].lc);
    update(x);return x;
  }
  void split(int x,int &a,int &b,int k)
  {
    if(!x){a=0;b=0;return;}
    if(t[t[x].lc].size>=k)
    {
      t[b=++cnt]=t[x];
      split(t[x].lc,a,t[b].lc,k);
      update(b);
    }
    else
    {
      t[a=++cnt]=t[x];
      split(t[x].rc,t[a].rc,b,k-t[t[x].lc].size-1);
      update(a);
    }
  }
  ll cal(ll x,ll y){return A*x+B*y+C;}
  bool check(int x,point a,point b)
  {
    if(!x)return 0;
    ll t1=cal(a.x,a.y),t2=cal(b.x,b.y);
    return (t1<=0&&t2>=0)||(t1>=0&&t2<=0);
  }
  int qry(int x)
  {
    if(!x)return 0;
    int res,lc=t[x].lc,rc=t[x].rc;ll a,b,c,d;
    res=check(lc,t[lc].r,t[x].p)+check(rc,t[rc].l,t[x].p);
    a=cal(t[x].x1,t[x].y1);b=cal(t[x].x1,t[x].y2);
    c=cal(t[x].x2,t[x].y1);d=cal(t[x].x2,t[x].y2);
    if((a<0&&b<0&&c<0&&d<0)||(a>0&&b>0&&c>0&&d>0))return res;
    return res+qry(lc)+qry(rc);
  }
}T;

int main()
{
  int x0,y0,x,y,z,k,p;char ch;
  scanf("%d%d%d",&n,&m,&tp);
  for(int i=1;i<=n;i++)
  {
    scanf("%d%d",&x,&y);
    t[++cnt]=T.make(x,y);
    rt[0]=T.merge(rt[0],cnt);
  }
  for(int i=1;i<=m;i++)
  {
    scanf(" %c%d",&ch,&z);rt[i]=rt[z];
    if(ch=='H')
    {
      scanf("%d%d%d%d",&x0,&y0,&x,&y);
      if(tp)x0=get(x0),y0=get(y0),x=get(x),y=get(y);
      A=y;B=-x;C=(ll)x*y0-(ll)x0*y;
      printf("%d\n",pre=T.qry(rt[i]));
    }
    else
    {
      scanf("%d%d%d",&k,&x,&y);
      if(tp)x=get(x),y=get(y);
      t[p=++cnt]=T.make(x,y);
      int a=0,b=0;T.split(rt[i],a,b,k);
      rt[i]=T.merge(a,T.merge(p,b));
    }
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值