二进制分组

二进制分组思想很简单,就是把下标拆成几个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 }
BZOJ4140

[BZOJ2989]数列

树套树或者CDQ分治或者3D-Tree模板题,大家都用它来练二进制分组。

对插入时间二进制分组,然后就是一个二维数点问题,两组的合并最好用归并。

主席树是一个DAG,要注意删除与垃圾回收的实现。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 #define mp make_pair
 5 #define pb push_back
 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 7 using namespace std;
 8 
 9 const int N=200010,M=200000,K=65000,inf=1e9;
10 char op[10];
11 bool vis[N<<5];
12 int n,Q,top,tot,nd,a[N],rt[20][N],ls[N<<5],rs[N<<5],v[N<<5],stk[N<<5];
13 vector<pair<int,int> >t,ve[20];
14 
15 int get(){
16     if (top) { vis[stk[top]]=1; return stk[top--]; }
17         else{ vis[++nd]=1; return nd; }
18 }
19 
20 int que(int x,int y,int L,int R,int l,int r){
21     if (L==l && r==R) return v[y]-v[x];
22     int mid=(L+R)>>1;
23     if (r<=mid) return que(ls[x],ls[y],L,mid,l,r);
24     else if (l>mid) return que(rs[x],rs[y],mid+1,R,l,r);
25         else return que(ls[x],ls[y],L,mid,l,mid)+que(rs[x],rs[y],mid+1,R,mid+1,r);
26 }
27 
28 void mdf(int y,int &x,int L,int R,int k){
29     x=get(); ls[x]=ls[y]; rs[x]=rs[y]; v[x]=v[y]+1;
30     if (L==R) return;
31     int mid=(L+R)>>1;
32     if (k<=mid) mdf(ls[y],ls[x],L,mid,k);
33         else mdf(rs[y],rs[x],mid+1,R,k);
34 }
35 
36 void del(int &x){
37     if (!vis[x]) return;
38     stk[++top]=x; vis[x]=0;
39     del(ls[x]); del(rs[x]); v[x]=0; x=0;
40 }
41 
42 void ins(int x,int y){
43     ve[++tot].pb(mp(x,y)); mdf(0,rt[tot][1],1,M,y);
44     while (tot>1 && ve[tot-1].size()==ve[tot].size()){
45         int t1=0,t2=0,ed=ve[tot].size(); t.clear();
46         rep(i,1,ed) del(rt[tot-1][i]);
47         rep(i,1,ed) del(rt[tot][i]);
48         while (t1<(int)ve[tot-1].size() || t2<(int)ve[tot].size()){
49             if (t1<(int)ve[tot-1].size() && (t2==(int)ve[tot].size() || ve[tot-1][t1]<ve[tot][t2])) t.pb(ve[tot-1][t1++]);
50                 else t.pb(ve[tot][t2++]);
51             mdf(rt[tot-1][t1+t2-1],rt[tot-1][t1+t2],1,M,t[t1+t2-1].second);
52         }
53         ve[tot-1]=t; ve[tot].clear(); tot--;
54     }
55 }
56 
57 int que(int x,int y,int d){
58     int res=0;
59     rep(i,1,tot){
60         int k1=lower_bound(ve[i].begin(),ve[i].end(),mp(x+d,inf))-ve[i].begin();
61         int k2=lower_bound(ve[i].begin(),ve[i].end(),mp(x-d,0))-ve[i].begin();
62         res+=que(rt[i][k2],rt[i][k1],1,M,max(1,y-d),min(y+d,M));
63     }
64     return res;
65 }
66 
67 int main(){
68     freopen("bzoj2989.in","r",stdin);
69     freopen("bzoj2989.out","w",stdout);
70     scanf("%d%d",&n,&Q);
71     rep(i,1,n) scanf("%d",&a[i]),ins(a[i]+i,a[i]-i+K);
72     while (Q--){
73         scanf("%s",op); int x,y; scanf("%d%d",&x,&y);
74         if (op[0]=='Q') printf("%d
",que(a[x]+x,a[x]-x+K,y));
75             else a[x]=y,ins(a[x]+x,a[x]-x+K);
76     }
77     return 0;
78 }
BZOJ2989

[CF710F]String Set Queries

没有插入删除就是AC自动机模板题。发现删除操作就是另维护一个AC自动机计算已删除的串的贡献,于是只用考虑插入。

CDQ分治或二进制分组均可。AC自动机是有向图(还可能存在自环),所以同样要注意删除和垃圾回收的实现。

平时只建一个AC自动机时,根是0,所以fail和son有时会不赋值。建多棵的时候就要注意把所有为0的指针都要指向rt。

  1 #include<cstdio>
  2 #include<string>
  3 #include<vector>
  4 #include<iostream>
  5 #include<algorithm>
  6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
  7 #define pb push_back
  8 using namespace std;
  9 
 10 const int N=300100;
 11 string s;
 12 int m,op,top,nd,top1,top2,son[N][27],fail[N],v[N],stk[N],q[N];
 13 
 14 int get(){ return top ? stk[top--] : ++nd; }
 15 
 16 void ins(int x,string s){
 17     int len=s.length();
 18     rep(i,0,len-1){
 19         if (!son[x][s[i]-'a']) son[x][s[i]-'a']=get();
 20         x=son[x][s[i]-'a'];
 21     }
 22     v[x]++;
 23 }
 24 
 25 void del(int x){
 26     if (!fail[x]) return;
 27     fail[x]=v[x]=0; stk[++top]=x;
 28     rep(i,0,25) del(son[x][i]),son[x][i]=0;
 29 }
 30 
 31 struct Gr{
 32     vector<string>p; int tot,rt;
 33     void ins(string s){ p.pb(s); tot++; }
 34     void clear(){ del(rt); rt=get(); p.clear(); tot=0; }
 35     void build(){
 36         del(rt); rt=get();
 37         rep(i,0,tot-1) ::ins(rt,p[i]);
 38         int st=0,ed=0; fail[rt]=rt;
 39         rep(i,0,25) if (son[rt][i]) q[++ed]=son[rt][i],fail[son[rt][i]]=rt;
 40         while (st<ed){
 41             int x=q[++st];
 42             rep(i,0,25){
 43                 int c=son[fail[x]][i];
 44                 if (son[x][i]) fail[son[x][i]]=c?c:rt,q[++ed]=son[x][i];
 45                     else son[x][i]=c?c:rt;
 46             }
 47             v[x]+=v[fail[x]];
 48         }
 49     }
 50     int que(string s){
 51         int res=0,x=rt,len=s.length();
 52         rep(i,0,len-1){
 53             int c=s[i]-'a';
 54             if (son[x][c]) x=son[x][c];
 55             else{
 56                 int p=fail[x];
 57                 while (p!=rt && !son[p][c]) p=fail[p];
 58                 if (son[p][c]) x=son[p][c];
 59             }
 60             res+=v[x];
 61         }
 62         return res;
 63     }
 64 }A[20],B[20];
 65 
 66 void ins(string s){
 67     top1++; A[top1].clear(); A[top1].ins(s);
 68     while (top1>1 && A[top1].tot==A[top1-1].tot){
 69         rep(i,0,A[top1].tot-1) A[top1-1].ins(A[top1].p[i]);
 70         A[top1--].clear();
 71     }
 72     A[top1].build();
 73 }
 74 
 75 void del(string s){
 76     top2++; B[top2].clear(); B[top2].ins(s);
 77     while (top2>1 && B[top2].tot==B[top2-1].tot){
 78         rep(i,0,B[top2].tot-1) B[top2-1].ins(B[top2].p[i]);
 79         B[top2--].clear();
 80     }
 81     B[top2].build();
 82 }
 83 
 84 int que(string s){
 85     int res=0;
 86     rep(i,1,top1) res+=A[i].que(s);
 87     rep(i,1,top2) res-=B[i].que(s);
 88     return res;
 89 }
 90 
 91 int main(){
 92     freopen("710F.in","r",stdin);
 93     freopen("710F.out","w",stdout);
 94     for (scanf("%d",&m); m--; ){
 95         cin>>op>>s;
 96         if (op==1) ins(s);
 97         if (op==2) del(s);
 98         if (op==3) cout<<que(s)<<endl;
 99     }
100     return 0;
101 }
CF710F
原文地址:https://www.cnblogs.com/HocRiser/p/10734655.html