NOI Online 2021 岛屿探险

Description

每个物品有 \(a_i\)\(b_i\) 两个权,\(Q\) 次询问,给定 \(c\)\(d\),每次询问区间 \([l,r]\),求 \(\sum_{i=l}^{r} [a_i\oplus c\leq \min(b_i,d)]\)\(\oplus\) 表示异或。

Solution

首先想到的是将 \(\min\) 拆开,分为 \(d\leq b_i\)\(d>b_i\) 的部分。

先考虑 \(\max\{d_i\}\leq \min\{b_i\}\) 的部分分,那只需要求 \(\sum_{i=l}^{r} [a_i\oplus c\leq d]\),这个可以建一个可持久 01-Trie 来实现,判断 \(c\)\(d\) 的当前位,分类讨论 \(a_i\) 可能有哪些转移。也可以将询问离线,将询问拆成两个前缀作差,按右端点排序,就不用了可持久化了。

第二个是 \(\min\{d_i\}\geq \max\{b_i\}\)。也就是求 \(\sum_{i=l}^{r} [a_i\oplus c\leq b_i]\),这次有两个变量,非常不好搞。但是我们观察原式,如果查询区间是全局的话,交换 \((a,b)\)\((c,d)\) 是完全没有区别的,这是一个相当对称的式子。这就提示我们将询问和原序列交换,将询问当做序列,将序列当做询问,那么这就变成了第一种情况。唯一的区别是由于贡献永远是对询问做出的,所以这种情况应该是在 01-Trie 上打标记而不是询问。具体来说,首先将所有询问拆成两个前缀,按右端点排序。将所有的 \(c\) 插入 Trie。顺次枚举 \((a,b)\),在 Trie 上加标记。对一个询问的贡献就是 \(c\) 的根缀上的标记和。

考虑合并上述两种情况,如何计算贡献和 \(b\)\(d\) 的大小有关。考虑将询问和原序列合并,按 \(b\)\(d\) 从小到大排序,然后 CDQ 分治。那么对于左边的询问和右边的原序列,就是第一种情况;对于右边的询问和左边的原序列就是第二种情况。分别维护两个 Tire 计算贡献即可。

复杂度 \(O((n+q)\log(n+q)\log|V|)\)

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;

inline ll read(){
    ll x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

const int N=2e5+7;

struct Node{
    ll x,y;
    int q,l,r,id;
}a[N];

struct Trie{
    int s[2],sz;
}t[N*23];

struct Que{
    int pos,id,op;
    bool operator <(const Que &X) const{
        return pos<X.pos;
    }
};

int ans[N],tot=0,rt[N],L,R,tag[N*23];
inline bool Cmp1(const Node &X,const Node &Y){return X.y<Y.y;}
inline bool Cmp2(const Node &X,const Node &Y){return X.id<Y.id;}

ll Val,P;
void ins(int &id,int pre,int k=23){
    id=++tot;
    t[id]=t[pre]; t[id].sz++;
    int to=(Val>>k)&1;
    if(~k) ins(t[id].s[to],t[pre].s[to],k-1);
}

void ins(){
    int now=0;
    for(int i=23;~i;i--){
        int to=(Val>>i)&1;
        if(!t[now].s[to])
            t[now].s[to]=++tot;
        now=t[now].s[to];
    }
}

void Clear(int &id,int k=23){
    tot--;
    if(~k) Clear(t[id].s[(Val>>k)&1],k-1);
    t[id]=(Trie){{0,0},0}; id=0;
}

void Clear2(int id){
    if(t[id].s[0]) Clear2(t[id].s[0]);
    if(t[id].s[1]) Clear2(t[id].s[1]);
    t[id]=(Trie){{0,0},0};
    tag[id]=0;
}

int query(int id,int k=23){
    if(k==-1) return t[id].sz;
    if(!id) return 0;
    int x=(Val>>k)&1,y=(P>>k)&1;
    if(y) return x? (query(t[id].s[0],k-1)+t[t[id].s[1]].sz):(query(t[id].s[1],k-1)+t[t[id].s[0]].sz);
    else return x? query(t[id].s[1],k-1):query(t[id].s[0],k-1);
}

void modify(){
    int now=0;
    for(int i=23;~i;i--){
        int x=(Val>>i)&1,y=(P>>i)&1;
        if(y){
            if(x){
                if(t[now].s[1]) tag[t[now].s[1]]++;
                if(!t[now].s[0]) return ;
                else now=t[now].s[0];
            }else{
                if(t[now].s[0]) tag[t[now].s[0]]++;
                if(!t[now].s[1]) return ;
                else now=t[now].s[1];
            }
        }else{
            if(x){
                if(!t[now].s[1]) return ;
                else now=t[now].s[1];
            }else{
                if(!t[now].s[0]) return ;
                else now=t[now].s[0];
            }
        }
    }
    tag[now]++; 
}

int query2(ll x){
    int ret=0,now=0;
    for(int i=23;~i;i--){
        int to=(x>>i)&1;
        now=t[now].s[to];
        ret+=tag[now];
    }
    return ret;
}

void CDQ(int l,int r){
    if(l==r) return ;
    int mid=(l+r)>>1;
    CDQ(l,mid),CDQ(mid+1,r);
    vector<int> V; int ret=0;
    sort(a+l,a+mid+1,Cmp2),sort(a+mid+1,a+r+1,Cmp2);
    for(int i=mid+1;i<=r;i++)
        if(!a[i].q) ++ret,Val=a[i].x,ins(rt[ret],rt[ret-1]),V.push_back(a[i].id);
    for(int i=l;i<=mid;i++)
        if(a[i].q){
            L=lower_bound(V.begin(),V.end(),a[i].l)-V.begin()+1;
            R=upper_bound(V.begin(),V.end(),a[i].r)-V.begin();
            if(L>R) continue;
            Val=a[i].x,P=a[i].y;
            ans[a[i].id]+=query(rt[R])-query(rt[L-1]);
        }
    ret=0;
    for(int i=mid+1;i<=r;i++)
        if(!a[i].q) ++ret,Val=a[i].x,Clear(rt[ret]);
    vector<Que> Q;
    for(int i=mid+1;i<=r;i++)
        if(a[i].q){
            Val=a[i].x,ins();
            Q.push_back((Que){a[i].r,i,1});
            Q.push_back((Que){a[i].l-1,i,-1});
            Val=a[i].x,ins();
        }
    sort(Q.begin(),Q.end());
    int j=0;
    for(int i=l;i<=mid;i++)
        if(!a[i].q){
            while(j<Q.size()&&Q[j].pos<a[i].id)
                ans[a[Q[j].id].id]+=Q[j].op*query2(a[Q[j].id].x),j++;
            Val=a[i].x,P=a[i].y,modify();
        }
    while(j<Q.size())
        ans[a[Q[j].id].id]+=Q[j].op*query2(a[Q[j].id].x),j++;
    Clear2(0),tot=0;
}

int main(){
    int n=read(),q=read();
    for(int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read(),a[i].id=i;
    for(int i=1;i<=q;i++)
        a[n+i].l=read(),a[n+i].r=read(),
        a[n+i].x=read(),a[n+i].y=read(),a[n+i].q=1,a[n+i].id=i;
    sort(a+1,a+1+n+q,Cmp1),CDQ(1,n+q);
    for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
posted @ 2021-08-22 08:43  Kreap  阅读(43)  评论(0编辑  收藏  举报