1. 程式人生 > >[學習筆記]二進位制分組

[學習筆記]二進位制分組

1、CF710F String Set Queries

AC自動機+二進位制分組。

二進位制分組好像可以搞很多強制線上的題目,比如這題。

利用二進位制分組思想,維護一個 \(siz\) 從大到小的單調棧,若 \(siz_{top}=siz_{top-1}\) 就一直暴力合併兩個 \(AC\) 自動機並求出新 \(AC\) 自動機的 \(fail\) 指標。

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
int n;char s[maxn];

struct Aho_Corasick_Automaton{
    int ch[maxn][26],vis[maxn][26],fail[maxn],num[maxn],sum[maxn],siz[maxn],rt[maxn],top,cnt;
    void merge(int &x,int y){
        if(x==0||y==0){
            x=x+y;
            return ;
        }
        num[x]+=num[y];
        for(int i=0;i<26;i++) merge(ch[x][i],ch[y][i]);
    }
    void getfail(int p){
        queue<int> q;
        for(int c=0;c<26;c++){
            if(ch[p][c]){
                vis[p][c]=ch[p][c];
                fail[ch[p][c]]=p;
                q.push(vis[p][c]);
            }
            else vis[p][c]=p;
        }
        while(!q.empty()){
            p=q.front(),q.pop();
            for(int c=0;c<26;c++){
                if(ch[p][c]){
                    vis[p][c]=ch[p][c];
                    fail[ch[p][c]]=vis[fail[p]][c];
                    q.push(vis[p][c]);
                }
                else vis[p][c]=vis[fail[p]][c];
            }
            sum[p]=num[p]+sum[fail[p]];
        }
    }
    void insert(char *s){
        rt[++top]=++cnt;siz[top]=1;
        int len=strlen(s+1),p=rt[top],c;
        for(int i=1;i<=len;i++){
            c=s[i]-'a';
            if(!ch[p][c]) ch[p][c]=++cnt;
            p=ch[p][c];
        }
        num[p]=1;
        while(siz[top-1]==siz[top]){
            merge(rt[top-1],rt[top]);
            siz[top-1]+=siz[top];top--;
        }
        getfail(rt[top]);
    }
    int query(char *s){
        int ans=0,len=strlen(s+1),p,c;
        for(int i=1;i<=top;i++){
            p=rt[i];
            for(int j=1;j<=len;j++){
                c=s[j]-'a';p=vis[p][c];
                ans+=sum[p];
            }
        }
        return ans;
    }
}A,B;

int main()
{
    scanf("%d",&n);
    int op;
    while(n--){
        scanf("%d%s",&op,s+1);
        if(op==1) A.insert(s);
        if(op==2) B.insert(s);
        if(op==3) printf("%d\n",A.query(s)-B.query(s));
        fflush(stdout);
    }
    return 0;
}

2、bzoj2989 數列

離線做法:\(CDQ\) 分治。

線上做法:主席樹+二進位制分組。

暴力合併兩棵主席樹,別忘弄一個回收空間的垃圾桶 \(rub\)

然後我兩點多的時候第一次提交 \(RE\),本地測起來 \(MLE\),調了一個多小時,最後發現垃圾桶寫錯了。。。

\(Code\ Below:\)

#include <bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int maxn=200000+10;
const int lim=100000;
const int inf=0x3f3f3f3f;
int n,q,a[maxn],rt[20][maxn],siz[20],top;
int L[maxn<<5],R[maxn<<5],sum[maxn<<5],rub[maxn<<5],tot,cnt;
vector<pii> v[20];bool vis[maxn<<5];

inline void read(int &x){
    x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    if(f==-1) x=-x;
}
inline void print(int x){
    if(x<0){putchar('-');x=-x;}
    if(x==0) putchar('0');
    static int sta[40],Top=0;
    while(x) sta[++Top]=x%10,x/=10;
    while(Top) putchar(sta[Top--]+'0');
    putchar('\n');
}

int newnode(){
    vis[rub[tot]]=0;
    return rub[tot--];
}

void update(int &now,int pre,int l,int r,int x){
    now=newnode();
    L[now]=L[pre];R[now]=R[pre];sum[now]=sum[pre]+1;
    if(l == r) return ;
    int mid=(l+r)>>1;
    if(x <= mid) update(L[now],L[pre],l,mid,x);
    else update(R[now],R[pre],mid+1,r,x);
}

int query(int u,int v,int Le,int Ri,int l,int r){
    if(Le <= l && r <= Ri) return sum[v]-sum[u];
    int mid=(l+r)>>1,ans=0;
    if(Le <= mid) ans+=query(L[u],L[v],Le,Ri,l,mid);
    if(Ri > mid) ans+=query(R[u],R[v],Le,Ri,mid+1,r);
    return ans;
}

void del(int x){
    if(vis[x]) return ;
    vis[x]=1;rub[++tot]=x;
    if(L[x]) del(L[x]);
    if(R[x]) del(R[x]);
    sum[x]=L[x]=R[x]=0;
}

void build(int x){
    for(int i=1;i<=siz[x];i++){
        rt[x][i]=rt[x][i-1];
        update(rt[x][i],rt[x][i],1,n+lim,v[x][i-1].second);
    }
}

void insert(int x,int y){
    siz[++top]=1;v[top].push_back(mp(x,y));build(top);
    while(siz[top-1]==siz[top]){
        for(int i=0;i<siz[top];i++) v[top-1].push_back(v[top][i]);
        sort(v[top-1].begin(),v[top-1].end());
        v[top].clear();
        for(int i=1;i<=siz[top];i++){
            if(rt[top-1][i]) del(rt[top-1][i]);
            if(rt[top][i]) del(rt[top][i]);
        }
        siz[top-1]+=siz[top];build(--top);
    }
}

int ask(int x,int y,int k){
    int l,r,ans=0;
    for(int i=1;i<=top;i++){
        l=upper_bound(v[i].begin(),v[i].end(),mp(x-k,0))-v[i].begin();
        r=upper_bound(v[i].begin(),v[i].end(),mp(x+k,inf))-v[i].begin();
        if(l<=r) ans+=query(rt[i][l],rt[i][r],max(1,y-k),min(n+lim,y+k),1,n+lim);
    }
    return ans;
}

int main()
{
    read(n),read(q);
    for(int i=1;i<=200000*32;i++) rub[++tot]=i,vis[i]=1;
    for(int i=1;i<=n;i++) read(a[i]),insert(a[i]-i+n,a[i]+i);
    char op[10];int x,k;
    while(q--){
        scanf("%s",op+1);read(x),read(k);
        if(op[1]=='M') a[x]=k,insert(a[x]-x+n,a[x]+x);
        else print(ask(a[x]-x+n,a[x]+x,k));
    }
    return 0;
}