莫队算法

E. Easy String Problem

题意:好懒的人啊,自己看题~~~

题解:这道题正解是莫队,那么我们怎么可以想到莫队的呢?有以下几个特征点可以指引我们想到莫队,首先是数据规模\(1e5\),这个\(1e5\)其实对应的算法是不多的,比如莫队,分块,块状链表等等,第二是区间询问是莫队的典型特征。其实根据数据规模能得到很多信息的,毕竟出题人也不傻~~~。

​ 我们由此可以得到一个初步判断,他就是莫队,基于这个去思考这道题的性质将会容易很多。 看到这不妨停下来自己想想,莫队需要哪些性质,而我们怎么挖掘与其相关的性质?

​ 不知道聪明的你想到了没有,其实很简单,观察12321,我们假设询问区间是3 3,我们使用正难则反的思路,左边的坐标有三个选择,右边同样是三个,所以可以得到\(3 * 3 = 9\)这个答案,我们考虑如何去重,可以发现,删去2 3和3 4一样,1 3和3 5一样,发现好像与区间两旁的重复部分有关系,再比如11312,同样询问3 3,答案同样是 $ 9 - 2 $,我们可以发现和两边重复数字的个数乘积有关系,没错一个询问区间答案就是全部取法减去两边重复数字个数的乘积。我们通过莫队可以轻松维护~~~

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define ll long long

const int N = 1e5+5;
int a[N];
int len;
ll ans[N];
int n,m;

struct Query{
  int id,l,r;
}q[N];

int get(int x){
  return x/len;
}

int cntr[N];
int cntl[N];

void addr(int x,ll& quan){
  cntr[x]--;
  quan -= cntl[x];
}

void delr(int x,ll& quan){
  cntr[x]++;
  quan += cntl[x];
}

void addl(int x,ll& quan){
  cntl[x]--;
  quan -= cntr[x];
}

void dell(int x,ll& quan){
  cntl[x]++;
  quan += cntr[x];
}

int main(){
  cin >> n;
  len = max(1,(int)sqrt(n));
  for(int i=1;i<=n;i++)cin >> a[i];
  cin >> m;
  rep(i,0,m){
    int l,r;cin >> l >> r;
    q[i] = {i,l,r};
  }
  sort(q,q+m,[&](Query a,Query b){
    int i = get(a.l), j = get(b.l);
    if(i!=j)return i < j;
    return a.r < b.r;
  });
  int L = 1, R = n;
  ll quan = 0;
  rep(i,0,m){
    int l = q[i].l,r = q[i].r,id = q[i].id;
    while(R < r)addr(a[++R],quan);
    while(R > r)delr(a[R--],quan);
    while(L > l)addl(a[--L],quan);
    while(L < l)dell(a[L++],quan);
//cout << quan << " " << l << " " << r  << endl;
    ans[id] = 1ll * l * (n + 1 - r) - quan;
  }
  rep(i,0,m){
    printf("%lld\n",ans[i]);
  }
}
posted @ 2022-05-03 20:41  mafengfa  阅读(47)  评论(0)    收藏  举报