二进制分组思想很简单,就是把下标拆成几个2的次幂的和,对每个2的次幂维护答案,复杂度是暴力重构一组的复杂度乘log(如果可以归并可能会少个log)。
这里其实想整理下一些修改独立的数据结构题的套路。
离线算法:
(1) 只有插入:CDQ分治。
(2) 有删除:
支持删除操作:CDQ分治。
不支持删除但支持按插入反序撤销:线段树分治。
不支持删除与撤销:时间倒流。
(3)每次询问的是某个修改区间内的所有修改都生效时的答案:分治+前后缀和。
(4)答案可二分、修改的贡献具有交换律、结合律、可加性:整体二分。
(5)每次询问的是一个区间:莫队、回滚莫队。
在线算法: (1) 随机。 (2) 二进制分组。
二进制分组题表:
[BZOJ4140]共点圆加强版
论文题。化下式子发现是一个半平面,于是问题变为在线插入点并询问某个半平面内是否有点。维护点集的凸包,每次合并两组的时候暴力重构即可。
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 #define pb push_back 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=500100; 9 double x,y; 10 int n,ans,top,op; 11 struct P{ double x,y; }a[N]; 12 bool operator <(const P &a,const P &b){ return a.x==b.x ? a.y<b.y : a.x<b.x; } 13 14 double sl(P a,P b){ 15 if (a.x==b.x) return a.y>b.y ? 1e18 : -1e18; 16 return (a.y-b.y)/(a.x-b.x); 17 } 18 19 struct Gr{ 20 vector<P>p,q; int tot,top; 21 void ins(P x){ tot++; p.pb(x); } 22 void clear(){ p.clear(); q.clear(); tot=top=0; } 23 24 void build(){ 25 sort(p.begin(),p.end()); top=1; q.clear(); q.pb(p[0]); 26 rep(i,1,tot-1){ 27 while (top>1 && sl(q[top-1],q[top-2])-sl(q[top-1],p[i])>=0) top--,q.pop_back(); 28 q.pb(p[i]); top++; 29 } 30 } 31 32 bool que(double x,double y){ 33 double k=-x/y; int l=1,r=top-1,res=0; 34 while (l<=r){ 35 int mid=(l+r)>>1; 36 if (sl(q[mid],q[mid-1])<=k) l=mid+1,res=mid; else r=mid-1; 37 } 38 return 2*x*q[res].x+2*y*q[res].y>=x*x+y*y; 39 } 40 }B[50]; 41 42 void ins(double x,double y){ 43 B[++top].ins((P){x,y}); 44 while (top>1 && B[top].tot==B[top-1].tot){ 45 rep(i,0,B[top].tot-1) B[top-1].ins(B[top].p[i]); 46 B[top--].clear(); 47 } 48 B[top].build(); 49 } 50 51 bool que(double x,double y){ 52 if (!top) return 0; 53 rep(i,1,top) if (!B[i].que(x,y)) return 0; 54 return 1; 55 } 56 57 int main(){ 58 scanf("%d",&n); 59 rep(i,1,n){ 60 scanf("%d%lf%lf",&op,&x,&y); x+=ans; y+=ans; 61 if (!op) ins(x,y); 62 else if (que(x,y)) ans++,puts("Yes"); else puts("No"); 63 } 64 return 0; 65 }