凸包专题大概是我做的最吃shi考验代码能力的专题……
然后……大概我们的凸包可以分为静态凸包和动态凸包,从功能上可以分为决策性的凸包和计算几何性的凸包
其实没有多少区别,打就好了
静态凸包啥的我就不介绍怎么打了……
然后我推荐在弹栈的时候用叉积而不是暴力算斜率,那样讨论好多啊……
另外,一般我们不会被卡弹栈……但如果被卡时间的话,我们可以考虑二分弹栈,这样并没有什么问题……
然后我们来看看凸包可以干什么:
然后我一开始想歪了……我想直接把(x,y)和(p,q)当作点的坐标然后去凸壳查找
但是这样的决策点是无法定义最优的……在不同的询问点中最优是不同的……很是尴尬
然后我这么弱当然%了题解啊……
所以我们可以考虑01分数规划,二分答案k,把原来的式子变形得到
$ y_{i}-k*x_{i}+q_{i}-k*p_{i}>=0 $
然后询问的时候,我们这就是维护一个上凸壳拿一条直线卡一下
可以联系一下高中线性规划的知识
然后我们可以用熟练剖分来解决这个问题,对于链上log次询问我们取最优的最优点即可
这样的复杂度……是$mlog^{4}n$的,但是由于没啥常数,并且数据范围还很小,所以说可以过30000的全部数据
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 #define N 30010
6 #define db double
7 int e,adj[N],n;
8 db x[N],y[N],p[N],q[N];
9 struct edge{int zhong,next;}s[N<<1];
10 inline void add(int qi,int zhong)
11 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
12 int deep[N],son[N],size[N],fa[N],dfn[N],num,top[N],anti[N];
13 inline void dfs1(int rt,int Vater)
14 {
15 fa[rt]=Vater;deep[rt]=deep[Vater]+1,size[rt]=1;
16 register int i,u;
17 for(i=adj[rt];i;i=s[i].next)
18 if((u=s[i].zhong)!=Vater)
19 {
20 dfs1(u,rt),size[rt]+=size[u];
21 if(size[u]>size[son[rt]])son[rt]=u;
22 }
23 }
24 inline void dfs2(int rt,int tp)
25 {
26 dfn[rt]=++num,top[rt]=tp;anti[num]=rt;
27 if(son[rt])dfs2(son[rt],tp);
28 register int i,u;
29 for(i=adj[rt];i;i=s[i].next)
30 if((u=s[i].zhong)!=fa[rt]&&u!=son[rt])dfs2(u,u);
31 }
32 struct pt
33 {
34 db x,y;pt(db a=0,db b=0){x=a,y=b;}
35 inline pt operator + (const pt &b) const {return pt(x+b.x,y+b.y);}
36 inline pt operator - (const pt &b) const {return pt(x-b.x,y-b.y);}
37 inline db operator * (const pt &b) const {return x*b.y-y*b.x;}
38 }Mem1[N<<4],*head1=Mem1,Mem2[N<<4],*head2=Mem2,sta[N];
39 struct node
40 {
41 node *ch[2];
42 pt *xy,*pq;
43 int lxy,lpq;
44 }*root,mem[N<<1];int tot;
45 inline bool mt1(const pt &a,const pt &b){return a.x==b.x?a.y<b.y:a.x<b.x;}
46 #define inf 0x7fffffff
47 inline double max(db a,db b){return a>b?a:b;}
48 inline int solve(pt *x,int len)
49 {
50 sort(x,x+len,mt1);
51 register int i,top=-1;
52 for(i=0;i<len;++i)
53 {
54 while(top>0&&(sta[top]-sta[top-1])*(x[i]-sta[top])>=0)--top;
55 sta[++top]=x[i];
56 }
57 for(i=0;i<=top;++i)x[i]=sta[i];
58 return top;
59 }
60 inline db query(pt *x,db k,int len)
61 {
62 if(len==0)return x[0].y-k*x[0].x;
63 int l=0,r=len-1,mi,ans=0;
64 pt q=pt(1,k),tmp;
65 while(l<=r)
66 {
67 mi=(l+r)>>1,tmp=x[mi+1]-x[mi];
68 if(tmp*q>=0)ans=mi,r=mi-1;
69 else l=mi+1;
70 }
71 return max(x[ans].y-k*x[ans].x,x[ans+1].y-k*x[ans+1].x);
72 }
73 inline node* build(int l,int r)
74 {
75 node *o=mem+(tot++);
76 o->xy=head1,o->pq=head2;
77 head1+=r-l+1,head2+=r-l+1;
78 for(int i=l;i<=r;++i)
79 o->xy[i-l]=pt(x[anti[i]],y[anti[i]]),o->pq[i-l]=pt(p[anti[i]],q[anti[i]]);
80 o->lxy=solve(o->xy,r-l+1),o->lpq=solve(o->pq,r-l+1);
81 if(l==r)return o;
82 register int mi=(l+r)>>1;
83 o->ch[0]=build(l,mi),o->ch[1]=build(mi+1,r);
84 return o;
85 }
86 inline double qxy(node *o,int l,int r,int L,int R,db k)
87 {
88 if(L<=l&&r<=R)return query(o->xy,k,o->lxy);
89 register int mi=(l+r)>>1;
90 db ret=-inf;
91 if(L<=mi)ret=qxy(o->ch[0],l,mi,L,R,k);
92 if(mi<R)ret=max(ret,qxy(o->ch[1],mi+1,r,L,R,k));
93 return ret;
94 }
95 inline double qpq(node *o,int l,int r,int L,int R,db k)
96 {
97 if(L<=l&&r<=R)return query(o->pq,k,o->lpq);
98 register int mi=(l+r)>>1;
99 db ret=-inf;
100 if(L<=mi)ret=qpq(o->ch[0],l,mi,L,R,k);
101 if(mi<R)ret=max(ret,qpq(o->ch[1],mi+1,r,L,R,k));
102 return ret;
103 }
104 #define eps 1e-5
105 inline db queryxy(int a,int b,db k)
106 {
107 db ret=-inf;
108 while(top[a]^top[b])
109 {
110 if(deep[top[a]]<deep[top[b]])a^=b,b^=a,a^=b;
111 ret=max(ret,qxy(root,1,n,dfn[top[a]],dfn[a],k)),a=fa[top[a]];
112 }
113 if(deep[a]<deep[b])a^=b,b^=a,a^=b;
114 return max(ret,qxy(root,1,n,dfn[b],dfn[a],k));
115 }
116 inline db querypq(int a,int b,db k)
117 {
118 db ret=-inf;
119 while(top[a]^top[b])
120 {
121 if(deep[top[a]]<deep[top[b]])a^=b,b^=a,a^=b;
122 ret=max(ret,qpq(root,1,n,dfn[top[a]],dfn[a],k)),a=fa[top[a]];
123 }
124 if(deep[a]<deep[b])a^=b,b^=a,a^=b;
125 return max(ret,qpq(root,1,n,dfn[b],dfn[a],k));
126 }
127 int main()
128 {
129 // freopen("Ark.in","r",stdin);
130 register int i,m,a,b;scanf("%d",&n);
131 for(i=1;i<=n;++i)scanf("%lf",&x[i]);
132 for(i=1;i<=n;++i)scanf("%lf",&y[i]);
133 for(i=1;i<=n;++i)scanf("%lf",&p[i]);
134 for(i=1;i<=n;++i)scanf("%lf",&q[i]);
135 for(i=1;i<n;++i)scanf("%d%d",&a,&b),add(a,b),add(b,a);
136 dfs1(1,0),dfs2(1,1),root=build(1,n);
137 db l,r,mi,ans;scanf("%d",&m);
138 while(m--)
139 {
140 scanf("%d%d",&a,&b);
141 l=0,r=1e5,ans=0;
142 while(r-l>eps)
143 {
144 mi=(l+r)/2;
145 if(queryxy(a,b,mi)+querypq(a,b,mi)>=0)l=mi,ans=mi;
146 else r=mi;
147 }
148 printf("%.4lf\n",ans);
149 }
150 }
还有比较shi的题目……
这个题你发现是“区间加等比数列,区间询问最大值”
所以很sad的是我们没办法用线段树……
那么我们考虑分块……毕竟树状数据结构不能用了
我们可以发现,如果我们区间加等比数列的话,按照下标为x坐标,当前美观度为y坐标建一个凸壳的话,
原来不在凸壳上面的点现在不会进入凸壳……如果公差>0,后面比他优的还比它优,公差<0前面比他优的还比它优……
所以我们可以再去查找凸包的最高点……
然后暴力重构……等等分块需要的操作
难调……最后改过了
二分和三分找最高点都是可以的
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <cmath>
4 using namespace std;
5 #define LL long long
6 #define L 350
7 #define N 100010
8 #define INF 0x3fffffffffffffffll
9 int belong[N],n,len,num[L],sta[L][L];
10 //#define int long long
11 LL a[N],a0[L],d[L],upg[L];
12 char B[1<<15],*S=B,*T=B;
13 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
14 inline int read()
15 {
16 int x=0,f=1;register char c=getc;
17 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getc;}
18 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
19 return x*f;
20 }
21 inline LL max(LL a,LL b){return a>b?a:b;}
22 inline int min(int a,int b){return a<b?a:b;}
23 inline LL calc(int id)
24 {
25 if(id==0)return -INF;
26 return a[id]+a0[belong[id]]+d[belong[id]]*(id-len*(belong[id]-1)-1)+upg[belong[id]];
27 }
28 inline void clear(int id)
29 {
30 for(int i=len*(id-1)+1;i<=min(n,len*id);++i)a[i]=calc(i);
31 a0[id]=d[id]=upg[id]=0;
32 }
33 inline LL chaji(LL a,LL b,LL c,LL d){return a*d-b*c;}
34 inline void build(int id)
35 {
36 register int i,top=0;
37 for(i=len*(id-1)+1;i<=min(n,len*id);++i)
38 {
39 while(top>1&&chaji(sta[id][top]-sta[id][top-1],calc(sta[id][top])-calc(sta[id][top-1]),i-sta[id][top],calc(i)-calc(sta[id][top]))>=0)--top;
40 sta[id][++top]=i;
41 }
42 num[id]=top;
43 }
44 inline LL query(int id)
45 {
46 int l=2,r=num[id],mi,ans=1;
47 while(l<=r)
48 {
49 mi=l+r>>1;
50 if(calc(sta[id][mi-1])<=calc(sta[id][mi]))
51 ans=mi,l=mi+1;
52 else r=mi-1;
53 }
54 return calc(sta[id][ans]);
55 }
56 inline void update(int l,int r,int val)
57 {
58 int ida=belong[l],idb=belong[r],i;
59 LL tmpa0=0;
60 if(ida==idb)
61 {
62 clear(ida);
63 for(i=l;i<=r;++i)tmpa0+=val,a[i]+=tmpa0;
64 build(ida);
65 return;
66 }
67 clear(ida);
68 for(i=l;i<=len*ida;++i)tmpa0+=val,a[i]+=tmpa0;
69 build(ida);
70 for(i=ida+1;i<idb;++i)a0[i]+=tmpa0+val,d[i]+=val,tmpa0+=(LL)len*val;
71 clear(idb);
72 for(i=len*(idb-1)+1;i<=r;++i)tmpa0+=val,a[i]+=tmpa0;
73 for(i=r+1;i<=min(n,len*idb);++i)a[i]+=tmpa0;
74 build(idb);
75 for(i=idb+1;i<=belong[n];++i)upg[i]+=tmpa0;
76 }
77 inline LL query(int l,int r)
78 {
79 LL ret=-INF;
80 int ida=belong[l],idb=belong[r],i;
81 if(ida==idb)
82 {
83 for(i=l;i<=r;++i)ret=max(ret,calc(i));
84 return ret;
85 }
86 for(i=l;i<=len*ida;++i)ret=max(ret,calc(i));
87 for(i=ida+1;i<idb;++i)ret=max(ret,query(i));
88 for(i=len*(idb-1)+1;i<=r;++i)ret=max(ret,calc(i));
89 return ret;
90 }
91 signed main()
92 {
93 //freopen("Ark.in","r",stdin);
94 register int i,opt,m,l,r,v;
95 n=read(),len=sqrt(n+0.5);
96 for(i=1;i<=n;++i)a[i]=read()+a[i-1],belong[i]=(i-1)/len+1;
97 for(i=1;i<=belong[n];++i)build(i);
98 m=read();
99 while(m--)
100 {
101 opt=read(),l=read(),r=read();
102 if(opt)printf("%lld\n",query(l,r));
103 else v=read(),update(l,r,v);
104 }
105 }
然后还有一道题目
这道题是高中对勾函数23333
我们可以简单的写一些式子啥的……然后我们可以计算出每个点的最优斜率是多少……
至于怎么算选手可以自己推一下,挺简单的……
然后我们会拿一堆斜率去尝试……这时候我们可以利用一个上凸壳来进行决策
从这个上凸壳的右侧往左跑,每一个点到下一个点之间都会有一个斜率区间,这一个斜率区间的最优决策点都是当前点
然后我们可以判断这个点的最优斜率是否在这个区间里面,然后进行一些判断即可
说起来挺简单的……实现的时候还是恶心了我一下的
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <cmath>
4 #include <algorithm>
5 using namespace std;
6 char B[1<<15],*S=B,*T=B;
7 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
8 inline int read()
9 {
10 int x=0;register char c=getc;
11 while(c<'0'||c>'9')c=getc;
12 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
13 return x;
14 }
15 #define N 1000010
16 #define inf 0x7fffffff
17 #define INF 0x7fffffffffffffffll
18 #define db double
19 #define LL long long
20 struct Vector
21 {
22 int x,y;db k;
23 Vector(int a=0,int b=0){x=a,y=b;k=((x!=0)?((db)y/x):(-inf));}
24 inline Vector operator + (const Vector &b) const{return Vector(x+b.x,y+b.y);}
25 inline Vector operator - (const Vector &b) const{return Vector(x-b.x,y-b.y);}
26 inline LL operator * (const Vector &b) const{return (LL)x*b.y-(LL)y*b.x;}
27 }pt[N],ans[N];
28 inline bool mt1(const Vector &a,const Vector &b)
29 {
30 return a.x==b.x?a.y<b.y:a.x>b.x;
31 }
32
33 int main()
34 {
35 // freopen("Ark.in","r",stdin);
36 register int i,n,top;n=read();
37 for(i=1;i<=n;++i)
38 pt[i].x=read(),pt[i].y=read();
39 sort(pt+1,pt+n+1,mt1);
40 for(i=1,top=0;i<=n;++i)
41 {
42 while(top>1&&(ans[top]-ans[top-1])*(pt[i]-ans[top])<0)--top;
43 ans[++top]=pt[i];
44 }
45 ans[top+1]=Vector(0,ans[top].y);
46 db l=-INF,r,k2,sum=INF;Vector tmp;
47 for(i=1;i<=top;++i)
48 {
49 r=l,l=(ans[i+1]-ans[i]).k;
50 k2=-sqrt((db)ans[i].y/ans[i].x);
51 if(l>=k2&&k2>=r)
52 sum=min(sum,ans[i].x+ans[i].y-ans[i].y/k2-ans[i].x*k2);
53 else sum=min(sum,ans[i].x+ans[i].y-ans[i].y/l-ans[i].x*l);
54 if(l>0)break;
55 }
56 printf("%.4f\n",sum);
57 }
然后我们来看一下动态的凸包……这种凸包大概有2种,一种是比较友好的只带插入的凸包,一种是又插入又删除的凸包
只带插入的我们可以用一个平衡树维护……按x坐标建树,然后存个子树最前点和最后点,然后在插入一个新点之后弹掉两边的点
弹法和静态的一样,只不过我们加了个平衡树而已
然后我们看个板子题好了
没有什么好解释的,直接打就是了,我用的无旋Treap
当然我第一次打的代码比较蠢蛋……抄代码不要抄我的……
代码:
1 #include <cmath>
2 #include <cstdio>
3 #include <cstring>
4 #include <cstdlib>
5 #include <iostream>
6 using namespace std;
7 #define N 100010
8 #define eps 1e-6
9 char B[1<<15],*S=B,*T=B;
10 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
11 inline int read()
12 {
13 int x=0;register char c=getc;
14 while(c<'0'||c>'9')c=getc;
15 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
16 return x;
17 }
18 #define db double
19 int n,m,q,cnt,nx,ny;
20 struct point
21 {
22 db x,y;
23 point(db a=0,db b=0){x=a,y=b;}
24 }pt[N];
25 inline int sign(db a){return (a>-eps)-(a<eps);}
26 #define inf 0x7fffffff
27 inline db k(point ida,point idb)
28 {
29 if(sign(ida.x-idb.x)==0)
30 return ida.y>idb.y?-inf:inf;
31 return (ida.y-idb.y)/(ida.x-idb.x);
32 }
33 inline db sqr(db a){return a*a;}
34 inline db dis(point a,point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
35 struct node
36 {
37 int key,size;
38 node *ch[2];point x;
39 node(){key=rand();}
40 inline void update(){size=ch[0]->size+1+ch[1]->size;}
41 }*null=new node(),*root,mem[N];
42 int opt[N<<1],id[N<<1],tot,top;
43 db ans[N<<1],sum;bool vis[N];
44 inline node* newnode(point x)
45 {
46 node *o=mem+(tot++);o->size=1;
47 o->ch[0]=o->ch[1]=null;o->x=x;
48 return o;
49 }
50 inline node* merge(node *a,node *b)
51 {
52 if(a==null)return b;
53 if(b==null)return a;
54 if(a->key>b->key){a->ch[1]=merge(a->ch[1],b),a->update();return a;}
55 else{b->ch[0]=merge(a,b->ch[0]),b->update();return b;}
56 }
57 #define D pair<node*,node*>
58 inline D split(node *o,int k)
59 {
60 if(o==null)return D(null,null);D y;
61 if(o->ch[0]->size>=k)y=split(o->ch[0],k),o->ch[0]=y.second,o->update(),y.second=o;
62 else y=split(o->ch[1],k-o->ch[0]->size-1),o->ch[1]=y.first,o->update(),y.first=o;
63 return y;
64 }
65 inline bool comp(point a,point b)
66 {return sign(a.x-b.x)==0?a.y<b.y:a.x<b.x;}
67 inline int get_rank(node *o,point a)
68 {
69 if(o==null)return 0;
70 return comp(a,o->x)?get_rank(o->ch[0],a):(get_rank(o->ch[1],a)+o->ch[0]->size+1);
71 }
72 inline node* get_end(node *o)
73 {while(o->ch[1]!=null)o=o->ch[1];return o;}
74 inline node* get_start(node *o)
75 {while(o->ch[0]!=null)o=o->ch[0];return o;}
76 inline void insert(point a)
77 {
78 int rk=get_rank(root,a),sz=root->size;
79 D tmp=split(root,rk),x1=D(null,null),x2=D(null,null);
80 node *x=newnode(a),*y,*tmp1=get_end(tmp.first),*tmp2=get_start(tmp.second);
81 if(k(tmp1->x,tmp2->x)>k(tmp1->x,a)){root=merge(tmp.first,tmp.second);return;}
82 db temp=dis(tmp1->x,tmp2->x);
83 if(rk>0&&rk<sz)sum-=temp;
84 x2=split(tmp.second,1);
85 while(x2.second!=null)
86 {
87 y=get_start(x2.second);
88 if(sign( k(a,x2.first->x) - k(x2.first->x,y->x) )<0)
89 sum-=dis(x2.first->x,y->x),x2=split(x2.second,1);
90 else break;
91 }
92 x1=split(tmp.first,--rk);
93 while(x1.first!=null)
94 {
95 y=get_end(x1.first);
96 if(sign(k(y->x,x1.second->x)-k(x1.second->x,a))<0)
97 sum-=dis(y->x,x1.second->x),x1=split(x1.first,--rk);
98 else break;
99 }
100 if(x1.second!=null)
101 sum+=dis(x1.second->x,a);
102 if(x2.first!=null)
103 sum+=dis(a,x2.first->x);
104 root=merge(merge(merge(x1.first,x1.second),x),merge(x2.first,x2.second));
105 }
106 int main()
107 {
108 register int i;
109 n=read(),nx=read(),ny=read(),cnt=m=read();
110 for(i=1;i<=m;++i)pt[i].x=read(),pt[i].y=read();
111 q=read();
112 for(i=1;i<=q;++i)
113 {opt[i]=read();if(opt[i]==1)id[i]=read(),vis[id[i]]=1;}
114 null->ch[0]=null->ch[1]=null;null->size=0;
115
116 root=newnode(point(nx,ny)),
117 root->ch[0]=newnode(point(0,0)),
118 root->ch[1]=newnode(point(n,0));
119 root->update();
120 sum=dis(point(0,0),point(nx,ny))+dis(point(n,0),point(nx,ny));
121 for(i=1;i<=m;++i)
122 if(!vis[i])insert(pt[i]);
123 for(i=q;i;--i)
124 if(opt[i]==1)insert(pt[id[i]]);
125 else ans[++top]=sum;
126 for(i=top;i;--i)printf("%.2f\n",ans[i]);
127 }
然后如果要支持插入删除的……我们有两种选择,一个是线段树分治
说的玄乎……只不过在分治的每一个单元存储了一些信息
我们从树根走到叶子,遇到的信息就是我们想要的全部信息
那么这个具体的题的话,我们可以发现这个点积是和投影有关的
所以我们可以三分凸壳,这个凸壳上最接近它的点就是最优点
我们沿用上面陶陶的难题那道题的思路,在log个可决策点里面选最优的
这种逐步逼近的思想很优秀,不少题都用了这种思想
也即不一次搞到最优解,而是通过若干次询问得到最终答案
那么代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 #include <cmath>
5 using namespace std;
6 #define inf 0x7fffffffll
7 #define INF 0x7fffffffffffffffll
8 #define N 400010
9 #define LL long long
10 #define sqr(a) ((LL)(a)*(a))
11 struct Vector
12 {
13 int x,y;
14 Vector(int a=0,int b=0){x=a,y=b;}
15 inline Vector operator + (const Vector &b)const {return Vector(x+b.x,y+b.y);}
16 inline Vector operator - (const Vector &b)const {return Vector(x-b.x,y-b.y);}
17 inline LL operator * (const Vector &b) const {return (LL)x*b.y-(LL)y*b.x;}
18 }vec[N];
19 inline LL Dot (const Vector &a,const Vector &b) {return (LL)a.x*b.x+(LL)a.y*b.y;}
20 int Mem[N<<5],*head=Mem,Mem2[N<<5],*head2=Mem2,Mem3[N<<5],*head3=Mem3;
21 struct node
22 {
23 node *ch[2];
24 int *sta,*ans1,*ans2,sz1,sz2;
25 }*root=NULL,mem[N<<1];int tot;
26 inline bool mt(const int &a,const int &b)
27 {return vec[a].x==vec[b].x?vec[a].y<vec[b].y:vec[a].x<vec[b].x;}
28 inline void insert(node *&o,int l,int r,const int &pos)
29 {
30 if(o==NULL)
31 o=mem+(tot++),o->ch[0]=o->ch[1]=NULL,
32 o->sta=head,o->ans1=head2,o->ans2=head3,
33 head+=r-l+1,head2+=r-l+1,head3+=r-l+1;
34 o->sta[pos-l]=pos;
35 if(pos==r)
36 {
37 int top=-1,len=r-l+1,i;
38 sort(o->sta,o->sta+len,mt);
39 for(top=-1,i=0;i<len;++i)
40 {
41 while(top>0&&(vec[o->ans1[top]]-vec[o->ans1[top-1]])*(vec[o->sta[i]]-vec[o->ans1[top]])>0)--top;
42 o->ans1[++top]=o->sta[i];
43 }o->sz1=top+1;
44 for(top=-1,i=0;i<len;++i)
45 {
46 while(top>0&&(vec[o->ans2[top]]-vec[o->ans2[top-1]])*(vec[o->sta[i]]-vec[o->ans2[top]])<0)--top;
47 o->ans2[++top]=o->sta[i];
48 }o->sz2=top+1;
49 }
50 if(l==r)return;
51 register int mi=(l+r)>>1;
52 if(pos<=mi)insert(o->ch[0],l,mi,pos);
53 else insert(o->ch[1],mi+1,r,pos);
54 }
55 inline LL max(const LL &a,const LL &b){return a>b?a:b;}
56 inline LL calc(const node *o,const int opt,const Vector q)
57 {
58 LL ret=-INF;
59 if(opt==1)
60 {
61 int l=0,r=o->sz1-1,ll,rr;
62 while(l+3<=r)
63 {
64 ll=(l+l+r)/3,rr=(l+r+r)/3;
65 if(Dot(vec[o->ans1[ll]],q)>Dot(vec[o->ans1[rr]],q))r=rr;
66 else l=ll;
67 }
68 for(int i=l;i<=r;++i)ret=max(ret,Dot(vec[o->ans1[i]],q));
69 return ret;
70 }
71 else
72 {
73 int l=0,r=o->sz2-1,ll,rr;
74 while(l+3<=r)
75 {
76 ll=(l+l+r)/3,rr=(l+r+r)/3;
77 if(Dot(vec[o->ans2[ll]],q)>Dot(vec[o->ans2[rr]],q))r=rr;
78 else l=ll;
79 }
80 for(int i=l;i<=r;++i)ret=max(ret,Dot(vec[o->ans2[i]],q));
81 return ret;
82 }
83 }
84 inline LL query(const node *o,int l,int r,const int &L,const int &R,const Vector &q)
85 {
86 if(L<=l&&r<=R)return q.y>0?calc(o,1,q):calc(o,2,q);
87 register int mi=(l+r)>>1;LL ret=-INF;
88 if(L<=mi)ret=query(o->ch[0],l,mi,L,R,q);
89 if(mi<R)ret=max(ret,query(o->ch[1],mi+1,r,L,R,q));
90 return ret;
91 }
92 int main()
93 {
94 // freopen("Ark.in","r",stdin);
95 register int n,i,a,b,l,r,isnot,cnt=0;char opt[3];LL ans=0;
96 scanf("%d%s",&n,opt);
97 isnot=(opt[0]!='E');
98 for(i=1;i<=n;++i)
99 {
100 scanf("%s%d%d",opt,&a,&b);
101 if(isnot)a^=(ans&inf),b^=(ans&inf);
102 if(opt[0]=='A')
103 vec[++cnt]=Vector(a,b),insert(root,1,n,cnt);
104 else
105 {
106 scanf("%d%d",&l,&r);
107 if(isnot)l^=(ans&inf),r^=(ans&inf);
108 printf("%lld\n",ans=query(root,1,n,l,r,Vector(a,b)));
109 }
110 }
111 }
至于另外一种,是用可持久化平衡树来维护区间的凸壳
然后用一个数据结构套起来……
在更新的时候我们可以找两个相邻凸壳的公切线,然后合并之,复杂度$logn$
说着简单……打起来很麻烦的
分类讨论贼多
打了一道NOI2017影分身,被uoj的特殊hack数据日死了
由于是猫树维护,所以似乎点集是一条直线
但是官方数据的20个都过了……
1 #pragma GCC optimize("O3")
2 #include <cstdio>
3 #include <ctime>
4 #include <cstring>
5 #include <cstdlib>
6 #include <iostream>
7 #include <algorithm>
8 using namespace std;
9 #define LL long long
10 #define inf 100000001
11 #define db double
12 #define N 100010
13 char B[1<<20],*cS=B,*cT=B;
14 #define getc (cS==cT&&(cT=(cS=B)+fread(B,1,1<<20,stdin),cS==cT)?0:*cS++)
15 inline int read()
16 {
17 int x=0,f=1;register char c=getc;
18 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getc;}
19 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
20 return x*f;
21 }
22 char O[1<<22],*cI=O;
23 inline void write(LL x)
24 {
25 if(!x)*cI++='0';
26 else
27 {
28 LL y;static char c[20], *i = c;
29 while (x)y=x/10,*i++=x-y*10+'0',x=y;
30 while (i != c)*cI++=*--i;
31 }
32 *cI++='\n';
33 }
34 struct Vector
35 {
36 int x,y;
37 Vector(int a=0,int b=0){x=a,y=b;}
38 inline Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);}
39 inline Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);}
40 inline LL operator * (const Vector &a){return (LL)x*a.y-(LL)y*a.x;}
41 }p[N*2];int cnt;
42 struct node
43 {
44 int key,lc,rc,it;
45 node *ch[2];LL sum;
46 node(){key=rand();}
47 inline void update()
48 {
49 sum=ch[0]->sum+ch[1]->sum;
50 lc=ch[0]->lc?ch[0]->lc:it;
51 if(ch[0]->rc)sum+=p[it]*p[ch[0]->rc];
52 rc=ch[1]->rc?ch[1]->rc:it;
53 if(ch[1]->lc)sum+=p[ch[1]->lc]*p[it];
54 }
55 }*null,mem[N<<7];int tot;
56 inline node* newnode(int id)
57 {
58 node *o=mem+(tot++);o->ch[0]=o->ch[1]=null;
59 o->lc=o->rc=o->it=id,o->sum=0;return o;
60 }
61 #define cop(a,b) ( ((b)==null)?(a=null):a=newnode(0),*a=*b )
62 inline node* merge(node *a,node *b)
63 {
64 node *rt;
65 if(a==null)return b;
66 else if(b==null)return a;
67 else if(a->key>b->key)
68 cop(rt,a),rt->ch[1]=merge(rt->ch[1],b),rt->update();
69 else
70 cop(rt,b),rt->ch[0]=merge(a,rt->ch[0]),rt->update();
71 return rt;
72 }
73 #define D pair<node*,node*>
74 db mid;
75 inline db cross(Vector a,Vector b,Vector c,Vector d)
76 {
77 db la=(b-d)*(c-d),lb=(c-d)*(a-d);
78 return (a.x+lb/(lb+la)*(b.x-a.x));
79 }
80 inline D join(node *x,node *y)
81 {
82 int a=x->it,b=y->it,la=x->ch[0]->rc,ra=x->ch[1]->lc,lb=y->ch[0]->rc,rb=y->ch[1]->lc;
83 D ret;node *o;
84 if(ra&&lb&&(p[ra]-p[b])*(p[a]-p[b])>=0&&(p[b]-p[a])*(p[lb]-p[a])>=0)
85 {
86 db mi=cross(p[a],p[ra],p[lb],p[b]);
87 if(mi<mid)
88 {
89 cop(o,x),ret=join(o->ch[1],y);
90 o->ch[1]=ret.first,o->update(),ret.first=o;
91 return ret;
92 }
93 else
94 {
95 cop(o,y),ret=join(x,o->ch[0]);
96 o->ch[0]=ret.second,o->update(),ret.second=o;
97 return ret;
98 }
99 }
100 if(la&&(p[a]-p[la])*(p[b]-p[a])>=0)return join(x->ch[0],y);
101 if(rb&&(p[b]-p[a])*(p[rb]-p[b])>=0)return join(x,y->ch[1]);
102 if(ra&&(p[ra]-p[b])*(p[a]-p[b])>=0)
103 {
104 cop(o,x),ret=join(o->ch[1],y);
105 o->ch[1]=ret.first,o->update(),ret.first=o;
106 return ret;
107 }
108 if(lb&&(p[b]-p[a])*(p[lb]-p[a])>=0)
109 {
110 cop(o,y),ret=join(x,o->ch[0]);
111 o->ch[0]=ret.second,o->update(),ret.second=o;
112 return ret;
113 }
114 // {
115 node *oo;
116 if(x->ch[1]==null)o=x;
117 else cop(o,x),o->ch[1]=null,o->update();
118 if(y->ch[0]==null)oo=y;
119 else cop(oo,y),oo->ch[0]=null,oo->update();
120 return D(o,oo);
121 // }
122 }
123 inline node* solve(node *a,node *b)
124 {
125 if(a==null)return b;
126 if(b==null)return a;
127 mid=(p[a->rc].x+p[b->lc].x)*0.5;
128 D x=join(a,b);
129 return merge(x.first,x.second);
130 }
131 inline void init()
132 {
133 null=new node();null->ch[0]=null->ch[1]=null;
134 null->sum=0;null->lc=null->rc=null->it=0;
135 }
136 int n,m,Tot,aski[110],tmp[110];
137 struct tree{tree *ch[2];node **sta;}Mem[N<<2];
138 inline bool mt(const int &a,const int &b)
139 {return p[a].x==p[b].x?p[a].y<p[b].y:p[a].x<p[b].x;}
140 // inline void dfs(node *o)
141 // {
142 // if(o->ch[0]!=null)dfs(o->ch[0]);
143 // printf("%d(%d,%d)\n",o->it,p[o->it].x,p[o->it].y );
144 // if(o->ch[1]!=null)dfs(o->ch[1]);
145 // }
146 struct Segment_Tree
147 {
148 tree *root;
149 node *lv[N],**head,*la[N*18],*T;
150 int id[N],rk[N];
151 inline tree* build(int l,int r)
152 {
153 tree *o=Mem+(Tot++);
154 o->sta=head,head+=r-l+1;
155 if(l==r){o->sta[0]=lv[l];return o;}
156 register int i,mi=(l+r)>>1;
157 o->ch[0]=build(l,mi),o->ch[1]=build(mi+1,r);
158 o->sta[mi-l]=lv[mi],o->sta[mi+1-l]=lv[mi+1];
159 for(i=mi-1;i>=l;--i)o->sta[i-l]=solve(lv[i],o->sta[i-l+1]);
160 for(i=mi+2;i<=r;++i)o->sta[i-l]=solve(o->sta[i-l-1],lv[i]);
161 // printf("%d--%d\n",l,r );
162 // for(i=l;i<=r;++i)
163 // dfs(o->sta[i-l]),printf("\n\n\n\n");
164 return o;
165 }
166 inline void build(int op)
167 {
168 register int i;head=la;
169 for(i=1;i<=n;++i)id[i]=op+i;
170 sort(id+1,id+n+1,mt);
171 for(i=1;i<=n;++i)rk[id[i]-op]=i;
172 for(i=1;i<=n;++i)lv[i]=newnode(id[i]);
173 root=build(1,n);
174 }
175 inline void query(tree *o,int l,int r,int L,int R)
176 {
177 register int mi=(l+r)>>1;
178 if(L<=mi&&mi<=R)
179 T=solve(T,o->sta[L-l]),T=solve(T,o->sta[R-l]);
180 else if(R<=mi)query(o->ch[0],l,mi,L,R);
181 else query(o->ch[1],mi+1,r,L,R);
182 }
183 inline LL getans(int op,int ge,Vector &l,Vector &r)
184 {
185 register int i,last,temp=tot;T=null;
186 for(i=1;i<=ge;++i)tmp[i]=rk[aski[i]];
187 sort(tmp+1,tmp+ge+1);
188 for(i=last=1;i<=ge;last=tmp[i]+1,++i)
189 if(tmp[i]>last)query(root,1,n,last,tmp[i]-1);
190 if(last<=n)query(root,1,n,last,n);
191 l=p[T->lc],r=p[T->rc];
192 tot=temp;
193 return T->sum+l*r;
194 }
195 }root1,root2;
196 int main()
197 {
198 // freopen("Ark.in","r",stdin);
199 // freopen("phantom.in","r",stdin);
200 // freopen("phantom.out","w",stdout);
201 p[0].x=p[0].y=-inf,init(),n=read(),m=read();
202 register int i,j,ge;LL ans=n-1;
203 for(i=1;i<=n;++i)p[i].x=read(),p[i].y=read();
204 root1.build(0);
205 for(i=1;i<=n;++i)p[i+n].x=p[i].x,p[i+n].y=-p[i].y;
206 // printf("%d %d\n",tot,(N<<7)-tot );
207 // return 0;
208 root2.build(n);
209 Vector upl,upr,downl,downr;
210 while(m--)
211 {
212 for(ge=read(),i=1;i<=ge;++i)aski[i]=(read()%n+ans)%n+1;
213 ans=root1.getans(0,ge,upl,upr)+root2.getans(n,ge,downl,downr);
214 downl.y*=-1,downr.y*=-1;
215 ans+=(downr-downl)*(upr-downl)+(upr-downl)*(upl-downl),write(ans);
216 }
217 fwrite(O,1,cI-O,stdout);
218 }
在这之后……然后我们可以看另外两道凸壳的问题,他们没有使用搞基数据结构,而是使用了cdq分治
其中一个我似乎写过题解了
另外一道题……也是一个斜率优化的题目,在这里写一下
推式子的过程我们可以略过了……那个dp还是很简单的,暴力dp是枚举每个祖先去更新自己
然后我们考虑dp优化,然后推一个斜率的式子
接着我们通过点分治来优化这个思想,因为它的形态结构很好……
大致步骤是这样的
1.找到重心
2.对重心的原树父亲联通块递归执行此操作
3.dfs重心的儿子,得到需要更新的点
4.然后我们把它们按照“能更新他们的最浅深度”按从大到小排序,这样就单调了
5.然后我们维护从当前重心往他的原树祖先的一个决策凸壳,用推出来的斜率式子更新
6.递归其余的儿子联通块们
大概就是这样的……当时我脑残了一波,排序的关键字搞错了
后来才想明白……其实这个是一个cdq思想的点分治啊233
代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 using namespace std;
5 char B[1<<15],*S=B,*T=B;
6 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
7 template <typename _T>
8 inline _T read()
9 {
10 _T x=0;register char c=getc;
11 while(c<'0'||c>'9')c=getc;
12 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
13 return x;
14 }
15 #define LL long long
16 #define N 200010
17 #define inf 100000000000000000ll
18 int n,e,adj[N],fa[N],maxsize[N],size[N];
19 LL limit[N],p[N],q[N],f[N],deep[N];
20 struct edge{int zhong,next;LL val;bool ban;}s[N];
21 inline void add(int qi,int zhong,LL val)
22 {s[++e].zhong=zhong;s[e].next=adj[qi];s[e].ban=0;s[e].val=val;adj[qi]=e;}
23 inline void dfs0(int rt)
24 {
25 for(int i=adj[rt];i;i=s[i].next)
26 deep[s[i].zhong]=deep[rt]+s[i].val,dfs0(s[i].zhong);
27 }
28 inline int max(int a,int b){return a>b?a:b;}
29 inline LL min(LL a,LL b){return a<b?a:b;}
30 inline void dfs1(int rt,int totsize,int &root)
31 {
32 size[rt]=1,maxsize[rt]=0;
33 for(int i=adj[rt];i;i=s[i].next)if(!s[i].ban)
34 dfs1(s[i].zhong,totsize,root),size[rt]+=size[s[i].zhong],
35 maxsize[rt]=max(maxsize[rt],size[s[i].zhong]);
36 maxsize[rt]=max(maxsize[rt],totsize-size[rt]);
37 if(maxsize[rt]<=maxsize[root])root=rt;
38 }
39 int sta[N],tot;
40 inline bool mt1(const int &a,const int &b)
41 {return deep[a]-limit[a]>deep[b]-limit[b];}
42 inline void dfs2(int rt)
43 {
44 sta[++tot]=rt;
45 for(int i=adj[rt];i;i=s[i].next)
46 if(!s[i].ban)dfs2(s[i].zhong);
47 }
48 int stack[N],top;
49 #define db double
50 inline double k(const int ida,const int idb)
51 {return (db)(f[ida]-f[idb])/(deep[ida]-deep[idb]);}
52 inline void insert(int id)
53 {
54 register int l=2,r=top,mi,ans=top+1;
55 while(l<=r)
56 {
57 mi=(l+r)>>1;
58 if( k( stack[mi],id ) > k( stack[mi],stack[mi-1] ) )ans=mi,r=mi-1;
59 else l=mi+1;
60 }
61 stack[top=ans]=id;
62 }
63 inline int query(int id)
64 {
65 if(top==1)return stack[1];
66 register int l=1,r=top-1,mi,ans=r;
67 while(l<=r)
68 {
69 mi=(l+r)>>1;
70 if(k(stack[mi],stack[mi+1])<p[id])ans=mi,r=mi-1;
71 else l=mi+1;
72 }
73 return (k(stack[ans],stack[ans+1])<p[id])?stack[ans]:stack[ans+1];
74 }
75 inline void solve(int rt,int siz)
76 {
77 if(siz<=1)return;
78 register int i,u,v,root=0,cur;
79 dfs1(rt,siz,root);
80 for(i=adj[root];i;i=s[i].next)s[i].ban=1;
81 solve(rt,siz-size[root]+1);
82 for(tot=0,i=adj[root];i;i=s[i].next)dfs2(s[i].zhong);
83 sort(sta+1,sta+tot+1,mt1);
84 for(cur=root,top=0,i=1;i<=tot;++i)
85 {
86 u=sta[i];
87 while(cur!=fa[rt]&&deep[u]-deep[cur]<=limit[u])
88 insert(cur),cur=fa[cur];
89 v=query(u);
90 if( ( double)f[v]+p[u]*1.0*(deep[u]-deep[v])+q[u] <=inf )f[u]=min(f[u],f[v]+p[u]*(deep[u]-deep[v])+q[u]);
91 }
92 for(i=adj[root];i;i=s[i].next)
93 u=s[i].zhong,solve(u,size[u]);
94 }
95 int main()
96 {
97 register int i;LL tmp;
98 memset(f,0x7f,sizeof(f)),f[1]=0;
99 n=read<int>(),maxsize[0]=n+1;
100 read<int>();
101 for(i=2;i<=n;++i)
102 fa[i]=read<int>(),tmp=read<LL>(),add(fa[i],i,tmp),
103 p[i]=read<LL>(),q[i]=read<LL>(),limit[i]=read<LL>();
104 dfs0(1),solve(1,n);
105 for(i=2;i<=n;++i)printf("%lld\n",f[i]);
106 }
大概我这阶段做的凸壳专题就是这么多……
我们抛开纯计算几何不谈,凸包的主要运用是优化决策,把一些$O(n)$转化为$O(logn)$
这种优化决策的思想很不错……
看到分式的时候我们可以优先考虑斜率,凸壳以及01分数规划
就是这些……
来源:oschina
链接:https://my.oschina.net/u/4354301/blog/4210441