BZOJ 3533 [SDOI2014]向量集 (线段树维护凸包)

题面:BZOJ传送门 洛谷传送门

容易想到这样一个结论

把每次插入向量$(z,w)$改为在坐标系内插入一个点$(z,w)$,并对这些点建出凸包。

每次询问向量$(x,y)$的答案,设一条直线$l$垂直于$(x,y)$,用l去切所有已知向量构成的凸包,第一个切到的点就是最优解,根据$y$的正负来决定是从上面切还是下面切

简单证明一下,向量点积相当于向量$(z,w)$在$(x,y)$上的投影*$|(x,y)|$,所以要找在$(x,y)$方向上最长的向量。

我们对向量$(x,y)$所在的直线作垂线,假设垂线经过点$(z,w)$,并交$y$轴于一点$p$,那么$p$点的纵坐标*向量$(x,y)$与$y$轴的夹角就是向量$(z,w)$的投影长,为了找最长的投影所以建凸包

然后就是维护凸包了,可题目要求强制在线,不能$CDQ$,询问$[l,r]$区间的凸包且$x$无序,可持久化splay维护凸包?

维护可持久化凸包一般是用线段树,线段树每个区间都挂一个凸包,询问$[l,r]$区间的凸包相当于区间查询

如果保证x有序,每个新来的点在线段树的每一层都插入,动态建出凸包。

所有询问不会超出已知的凸包范围!

每当线段树的一个区间被插满之后再建出凸包就行了。不会询问没建完的区间的凸包

每次在凸包上二分即可

时间$O(nlog^{2}n)$

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N1 400010
  5 #define dd double
  6 #define ll long long 
  7 using namespace std;
  8 
  9 const ll inf=0x3f3f3f3f3f3f3f3fll;
 10 const int zwz=1000000007;
 11 int gint()
 12 {
 13     int ret=0,fh=1;char c=getchar();
 14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 16     return ret*fh;
 17 }
 18 
 19 int n,Q;
 20 ll X[N1],Y[N1];
 21 int pos[N1];//id[N1];
 22 int cmpx0(int x,int y){ if(X[x]!=X[y]) return X[x]<X[y]; else return Y[x]<Y[y];}
 23 int cmpx1(int x,int y){ if(X[x]!=X[y]) return X[x]<X[y]; else return Y[x]>Y[y];}
 24 struct OP{int x,y,l,r;}op[N1];
 25 ll ask(int i,ll x,ll y){ return X[i]*x+Y[i]*y; }
 26 
 27 struct SEG{
 28 
 29 int stk0[20][N1],stk1[20][N1],ed0[N1<<2],ed1[N1<<2],que[N1];
 30 
 31 int cmp(int a,int b,ll x,ll y)
 32 {
 33     if(!a||!b) return a+b;
 34     return (X[a]*y+Y[a]*(-x)>X[b]*y+Y[b]*(-x))?a:b;
 35 }
 36 
 37 void build0(int *s,int &tp,int l,int r)
 38 {
 39     int i,j;
 40     for(i=l;i<=r;i++) que[i-l+1]=i;
 41     sort(que+1,que+r-l+2,cmpx0);
 42     for(j=1;j<=r-l+1;j++)
 43     {
 44         i=que[j];
 45         while(tp>1&&(Y[i]-Y[s[tp-1]])*(X[s[tp]]-X[s[tp-1]])>=(Y[s[tp]]-Y[s[tp-1]])*(X[i]-X[s[tp-1]]))
 46             tp--;
 47         s[++tp]=i;
 48     }
 49 }
 50 int qup(int *s,int l,int r,int rt,ll x,ll y)
 51 {
 52     if(!ed0[rt]) build0(s,ed0[rt],l,r);
 53     if(ed0[rt]==1) return s[1];
 54     l=2,r=ed0[rt]; int mid,ans=s[1];
 55     while(l<=r)
 56     {
 57         mid=(l+r)>>1; 
 58         if(1.0*(Y[s[mid]]-Y[s[mid-1]])/(X[s[mid]]-X[s[mid-1]])>=1.0*y/x) ans=s[mid],l=mid+1;
 59         else r=mid-1;
 60     }
 61     return ans;
 62 }
 63 int qmax(int L,int R,int l,int r,int rt,int D,ll x,ll y)
 64 {
 65     if(L<=l&&r<=R) return qup(stk0[D]+l-1,l,r,rt,x,y);
 66     int mid=(l+r)>>1,ans=0,tmp;
 67     if(L<=mid) tmp=qmax(L,R,l,mid,rt<<1,D+1,x,y),ans=cmp(ans,tmp,x,y);
 68     if(R>mid) tmp=qmax(L,R,mid+1,r,rt<<1|1,D+1,x,y),ans=cmp(ans,tmp,x,y);
 69     return ans;
 70 }
 71 
 72 
 73 void build1(int *s,int &tp,int l,int r)
 74 {
 75     int i,j;
 76     for(i=l;i<=r;i++) que[i-l+1]=i;
 77     sort(que+1,que+r-l+2,cmpx1);
 78     for(j=1;j<=r-l+1;j++)
 79     {
 80         i=que[j];
 81         while(tp>1&&(Y[i]-Y[s[tp-1]])*(X[s[tp]]-X[s[tp-1]])<=(Y[s[tp]]-Y[s[tp-1]])*(X[i]-X[s[tp-1]]))
 82             tp--;
 83         s[++tp]=i;
 84     }
 85 }
 86 int qdown(int *s,int l,int r,int rt,ll x,ll y)
 87 {
 88     if(!ed1[rt]) build1(s,ed1[rt],l,r);
 89     if(ed1[rt]==1) return s[1];
 90     l=2,r=ed1[rt]; int mid,ans=s[1];
 91     while(l<=r)
 92     {
 93         mid=(l+r)>>1;
 94         if(1.0*(Y[s[mid]]-Y[s[mid-1]])/(X[s[mid]]-X[s[mid-1]])<=1.0*y/x) ans=s[mid],l=mid+1;
 95         else r=mid-1;
 96     }
 97     return ans;
 98 }
 99 int qmin(int L,int R,int l,int r,int rt,int D,ll x,ll y)
100 {
101     if(L<=l&&r<=R) return qdown(stk1[D]+l-1,l,r,rt,x,y);
102     int mid=(l+r)>>1,ans=0,tmp;
103     if(L<=mid) tmp=qmin(L,R,l,mid,rt<<1,D+1,x,y),ans=cmp(ans,tmp,x,y);
104     if(R>mid) tmp=qmin(L,R,mid+1,r,rt<<1|1,D+1,x,y),ans=cmp(ans,tmp,x,y);
105     return ans;
106 }
107 
108 }s;
109 ll ans;
110 void decode(int &x){ x=x^(ans&0x7fffffff); }
111 
112 int de;
113 int main()
114 {
115     Q=gint(); char str[10],Str[10]; scanf("%s",Str);
116     int x,y,l,r,i,j,nn=0,a; 
117     for(i=1;i<=Q;i++)
118     {
119         scanf("%s",str);
120         if(str[0]=='A'){
121             op[i].x=gint(), op[i].y=gint(); nn++; pos[i]=nn; //id[nn]=i;
122         }else{
123             op[i].x=gint(), op[i].y=gint();
124             op[i].l=gint(), op[i].r=gint();
125         }
126     }
127     for(i=1;i<=Q;i++)
128     {
129         x=op[i].x; y=op[i].y; l=op[i].l; r=op[i].r;
130         if(!l&&!r){
131             if(Str[0]!='E') decode(x), decode(y); X[pos[i]]=x; Y[pos[i]]=y;
132         }else{
133             if(Str[0]!='E') decode(x), decode(y), decode(l), decode(r);
134             if(y>0) a=s.qmax(l,r,1,nn,1,0,-y,x); 
135             else a=s.qmin(l,r,1,nn,1,0,-y,x); 
136             ans=X[a]*x+Y[a]*y;
137             printf("%lld\n",ans);
138         }
139     }
140     return 0;
141 }

 

转载于:https://www.cnblogs.com/guapisolo/p/10359419.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值