2020牛客暑期多校训练营(第五场)H.Interval

题目链接

思路:固定一个右端点,对于不同左端点的区间 与值,最多只有log个不同的值。
那我们枚举右端点,算出所有这样的第一次出现不同值的左端点,然后在主席树上更新一下贡献。
注意去重。可以搞一个map来辅助实现上述的操作。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include 
using namespace std;
typedef long long LL;
const int N = 1e5 + 100;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
int n,a;
map<int,int>last,res,pos;
int T[N];
struct uzi{
  int l,r,sum;
}t[N*400];
int cnt;
#define mid (l+r>>1)
void add(int &x,int l,int r,int z,int val){
  t[++cnt]=t[x];
  t[cnt].sum+=val;
  x=cnt;
  if(l==r)return;
  if(z<=mid)add(t[x].l,l,mid,z,val);
  else add(t[x].r,mid+1,r,z,val);
}
int get(int x,int l,int r,int dx,int dy){
  if(l>=dx&&r<=dy) {
    return t[x].sum;
  }
  int tmp=0;
  if(dx<=mid)tmp+=get(t[x].l,l,mid,dx,dy);
  if(dy>mid)tmp+=get(t[x].r,mid+1,r,dx,dy);
  return tmp;
}
int main() {
  scanf("%d",&n);
  for(int i=1;i<=n;i++) {
    scanf("%d", &a);
    //记录区间 和 与值
    last[a]=i;
    T[i]=T[i-1];
    for(auto k:last){
      pos[k.fi&a]=max(pos[k.fi&a],k.se);
    }
    for(auto k:pos){
      if(res[k.fi]!=k.se){
        if(res[k.fi]) {
          add(T[i], 1, n, res[k.fi], -1);
        }
        add(T[i],1,n,k.se,1);
        res[k.fi]=k.se;
      }
    }
    last=pos;
    pos.clear();
  }
  int la=0;
  int Q;
  for(scanf("%d",&Q);Q;Q--){
    int l,r;
    scanf("%d%d",&l,&r);
    l=(l^la)%n+1;
    r=(r^la)%n+1;
    if(l>r)swap(l,r);
    printf("%d\n",la=get(T[r],1,n,l,r));
  }
  return 0;
}

你可能感兴趣的:(主席树)