2020牛客多校第五场H

题意:给你n个数,每次询问一个区间,问询问区间的子区间数取&去重后一共有多少个,强制在线

题解:首先由与是&操作,那么对于第i个位置,不管是往左还是往右取&都是一个不增区间,具有单调性了,那么我们其实是可以通过二分来求得对于第i个位置,它往一个方向取&后会在哪些地方改变,如果可以离线那么这道题就可以和REQ这道题类似的做法,但是本题强制在线,而且询问是区间询问可以考虑用主席树来进行维护,大体思路与REQ类似,对于每一个取&后的数,如果是第一次出现那就在对应时间戳的对应位置插入1代表他的贡献,如果已经出现,就将之前的贡献删除,在当前变小的位置插入贡献。解释一下这样做的正确性,感性理解,主席树继承了上一个位置的所有信息,那也就代表的当前位置的时间戳已经包含了之前的所有信息,但是在加入当前位置的信息后,它有部分信息是重复的,所以对于每一种出现的数都是将它放在了最靠近i的位置,在查询是就可以做到每一种&出来的值只进行的一个贡献统计,然后每次都是在当前的时间戳进行处理,之前已经维护的信息是没有变过的,所以查询时在ti[r]时间查询[l,r]的和时就是去重后的答案了。

    一个数转为二进制后,最多有log位,所以减小后的个数最多只有log位,加上二分,复杂度为平方级的,主席树也是log级的,所以总复杂度为(nlog2n+mlogn),可能常数偏大

AC代码:

#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define mid ((l+r)>>1)
#define lt (k<<1)
#define rt (k<<1)|1
using namespace std;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
int read() {
    int x = 0, w = 1;
    char ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-')ch = getchar();
    if (ch == '-')w = 0, ch = getchar();
    while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return w ? x : -x;
}
int va[maxn],tree[maxn<<2];
void build(int l,int r,int k){
    if(l==r){
        tree[k]=va[l];
        return ;
    }
    build(l,mid,lt),build(mid+1,r,rt);
    tree[k]=tree[lt]&tree[rt];
}
int qu(int l,int r,int k,int l1,int r1){
    if(l>=l1&&r<=r1) return tree[k];
    int ans=(1<<30)-1;
    if(l1<=mid) ans&=qu(l,mid,lt,l1,r1);
    if(r1>=mid+1) ans&=qu(mid+1,r,rt,l1,r1);
    return ans;
}
int num[maxn*73],ls[maxn*73],rs[maxn*73],ti[maxn],tot;
map m;
void add(int l,int r,int &now,int las,int w,int va){
    now=++tot;
    num[now]=num[las]+va;
    ls[now]=ls[las];
    rs[now]=rs[las];
    if(l==r) return ;
    if(mid>=w) add(l,mid,ls[now],ls[las],w,va);
    else add(mid+1,r,rs[now],rs[las],w,va);
}
int query(int l,int r,int now,int l1,int r1){
    if(now==0||!num[now]) return 0;
    if(l>=l1&&r<=r1) return num[now];
    int sum=0;
    if(mid>=l1) sum+=query(l,mid,ls[now],l1,r1);
    if(mid+1<=r1) sum+=query(mid+1,r,rs[now],l1,r1);
    return sum;
}
int main( ){
    int n=read();
    for(int a=1;a<=n;a++) va[a]=read();
    build(1,n,1);
    ti[0]=tot=0;
    for(int a=1;a<=n;a++){
        ti[a]=ti[a-1];
        if(m[va[a]]) add(1,n,ti[a],ti[a],m[va[a]],-1);
        add(1,n,ti[a],ti[a],a,1);
        m[va[a]]=a;
        int va1=va[a];
        while(1) {
            int l = 1, r = a - 1, ans = 0;
            while (l <= r) {
                if (qu(1, n, 1, mid, a) < va1) ans = mid, l = mid + 1;
                else r = mid - 1;
            }
            if (!ans) break;
            va1 = qu(1, n, 1, ans, a);
            if (m[va1]) add(1, n, ti[a], ti[a], m[va1], -1);
            m[va1] = ans;
            add(1, n, ti[a], ti[a], ans, 1);
        }
    }
    int q=read(),lastans=0;
    while(q--){
        int l=(read()^lastans)%n+1;
        int r=(read()^lastans)%n+1;
        if(l>r) swap(l,r);
        lastans=query(1,n,ti[r],l,r);
        printf("%d\n",lastans);
    }
}

 

你可能感兴趣的:(ACM,数据结构,算法)