UOJ #191. 【集训队互测2016】Unknown

Description

原题目名字是“我们仍未知道那天所看见的数据结构的名字”,由于原题目名太长就叫Unknown了……
我们,渐渐地长大了。在这缓缓逝去的季节里,屏幕上闪烁的字符,也在静静地变化着。
那个季节里编写的数据结构,叫什么名字来着呢?
慢慢地,OI渐渐地淡去。而我们则在不断成长,但是那个程序一定仍在某个时空里继续运行着。
Salroey忘了那个数据结构的名字和内容,但她却记得题目,于是她来寻求你的帮助。
有一个元素为向量的序列,下标从1开始,初始时为空,现在你需要支持三个操作:
1.在S的末尾添加一个元素(x,y)。
2.删除S的末尾元素。
3.询问下标在[l,r][l,r]区间内的元素中,(x,y)×Si的最大值。
其中××表示向量的叉积,(x1,y1)×(x2,y2)=x1y2−x2y1

Solution

由于 \(1\) 操作保证了向量一定是在 \(x\) 轴上方的
所以一定答案一定在凸包上,如果不是区间查询的话,维护一个凸包,然后三分就行了
如果有区间查询,用线段树维护就好了
对于每一个节点维护一个凸包,但是暴力合并复杂度是 \(O(n)\)
考虑一种神奇的做法....

由于这题点是在序列末端插入和在序列末端删除的
利用这个性质,我们可以等区间的点都满了再合并....但是可以在一个点附近反复横跳删除和添加
这样还是被卡了

所以用一个神奇的套路:
等该层的下一个节点满了再合并这个节点,这样的话,假设序列长度为 \(k\),那么每 \(k\) 次插入才会 \(O(k)\) 的合并一次,均摊是 \(O(1)\) 的....

复杂度 \(O(n*log^2)\),代码常数贼大...

#include<bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
typedef long long ll;
const int N=524289,mod=998244353,M=1050000;
struct P{
    int x,y;
    inline ll operator *(const P b){return 1ll*x*b.y-1ll*b.x*y;}
    inline bool operator <(const P b)const
    {return x<b.x||(x==b.x && y<b.y);}
    inline P operator -(const P b){return (P){x-b.x,y-b.y};}
}k;
vector<P>v[M];
int Q,T,op,pos=0,n=N-1,pre[25];
inline void upd(vector<P>&x,vector<P>&y,vector<P>&t){
    int i=0,j=0,sx=x.size(),sy=y.size();P w;
    while(i<sx || j<sy){
        if(j==sy||(i<sx&&x[i]<y[j]))w=x[i++];
        else w=y[j++];
        while(t.size()>=2 && (w-t[t.size()-1])*(t[t.size()-1]-t[t.size()-2])<=0)
            t.pop_back();
        t.push_back(w);
    }
}
inline void ins(int l,int r,int o,int sa,int t){
    if(l==r){v[o].push_back(k);return ;}
    int mid=(l+r)>>1;
    if(sa<=mid)ins(l,mid,ls,sa,t+1);
    else{
        ins(mid+1,r,rs,sa,t+1);
        if(sa==r){
            if(pre[t])upd(v[pre[t]<<1],v[pre[t]<<1|1],v[pre[t]]);
            pre[t]=o;
        }
    }
}
inline void Delet(int l,int r,int o,int sa,int t){
    if(l==r){vector<P>().swap(v[o]);return ;}
    int mid=(l+r)>>1;
    if(sa<=mid)Delet(l,mid,ls,sa,t+1);
    else{
        Delet(mid+1,r,rs,sa,t+1);
        if(sa==r)vector<P>().swap(v[o]),pre[t]=0;
    }
}
inline ll midit(vector<P>&v){
    int l=0,r=v.size()-1,ret=0,lm,rm;
    while(l<=r){
        lm=l+(r-l+1)/3;rm=r-(r-l)/3;
        if(k*(v[lm]-v[rm])>=0)ret=lm,r=rm-1;
        else ret=rm,l=lm+1;
    }
    return k*v[ret];
}
inline ll qry(int l,int r,int o,int sa,int se){
    if(sa<=l && r<=se && !v[o].empty())return midit(v[o]);
    int mid=(l+r)>>1;
    if(se<=mid)return qry(l,mid,ls,sa,se);
    else if(sa>mid)return qry(mid+1,r,rs,sa,se);
    return max(qry(l,mid,ls,sa,mid),qry(mid+1,r,rs,mid+1,se));
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>T;
  int x,y;
  while(1){
      int ans=0,pos=0;
      scanf("%d",&Q);
      if(Q==0)break;
      for(int i=0;i<M;i++) vector<P>().swap(v[i]);
      memset(pre,0,sizeof(pre));
      while(Q--){
          scanf("%d",&op);
          if(op==1)scanf("%d%d",&k.x,&k.y),ins(1,n,1,++pos,0);
          else if(op==2)Delet(1,n,1,pos--,0);
          else scanf("%d%d%d%d",&x,&y,&k.x,&k.y),ans^=(qry(1,n,1,x,y)%mod+mod)%mod;
      }
      cout<<ans<<endl;
  }
  return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/8516291.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值